From b5fd192f552290bc85ef90004770cb75d27e7a43 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Thu, 9 May 2024 22:37:24 +0100 Subject: [PATCH 01/15] copying kernels during the build --- recipe/build.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/recipe/build.sh b/recipe/build.sh index 0fedbe2..2940ec4 100755 --- a/recipe/build.sh +++ b/recipe/build.sh @@ -1,6 +1,9 @@ set -xe cp -rv "$RECIPE_DIR/../test" "$SRC_DIR/" +mkdir -p $SP_DIR/ccpi/cuda_kernels +cp -rv "$RECIPE_DIR/../src/Core/regularisers_GPU/cuda_kernels/" "$SP_DIR/ccpi/" + cmake -S "$SRC_DIR" -B "$RECIPE_DIR/../build_proj" -DBUILD_PYTHON_WRAPPER=ON -DCONDA_BUILD=ON -DBUILD_CUDA=ON -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DLIBRARY_INC=$CONDA_PREFIX -DCMAKE_INSTALL_PREFIX="$RECIPE_DIR/../install" cmake --build "$RECIPE_DIR/../build_proj" --target install $PYTHON -m pip install "$SRC_DIR/src/Python" From 07a090f1d76edeb6fdee6b690135431605e11ddf Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 13 May 2024 21:02:09 +0100 Subject: [PATCH 02/15] adding cupy kernels copy for windows --- recipe/bld.bat | 3 +++ 1 file changed, 3 insertions(+) diff --git a/recipe/bld.bat b/recipe/bld.bat index d17ce9f..6d311ef 100644 --- a/recipe/bld.bat +++ b/recipe/bld.bat @@ -4,6 +4,9 @@ if exist "%RECIPE_DIR%\..\build_proj" ( rd /s /q "%RECIPE_DIR%\..\build_proj" ) +mkdir "%SP_DIR%\ccpi\cuda_kernels" +ROBOCOPY /E "%RECIPE_DIR%\..\src\Core\regularisers_GPU\cuda_kernels\" "%SP_DIR%\ccpi\" + :: add -G "Visual Studio 16 2019" to the cmake command to specify the generator :: add -DCUDA_TOOLKIT_ROOT_DIR="C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v12.4" to the cmake command to specify the CUDA toolkit cmake -S "%SRC_DIR%" -B "%RECIPE_DIR%\..\build_proj" -DBUILD_PYTHON_WRAPPERS=ON -DCONDA_BUILD=ON -DBUILD_CUDA=ON -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DLIBRARY_INC="%CONDA_PREFIX%" -DCMAKE_INSTALL_PREFIX="%RECIPE_DIR%\..\install" From d2cb8ecc30485e57a76f40c83fa82ecb3e0e28ea Mon Sep 17 00:00:00 2001 From: dkazanc Date: Fri, 7 Jun 2024 17:17:54 +0100 Subject: [PATCH 03/15] adding cupy dependency and tests --- Installation.md | 2 +- recipe/meta.yaml | 4 +++- {tests_cupy => test}/conftest.py | 0 {tests_cupy => test}/test_ccpi_regularisers_cupy.py | 0 {tests_cupy => test}/test_data/tomo_standard.npz | Bin 5 files changed, 4 insertions(+), 2 deletions(-) rename {tests_cupy => test}/conftest.py (100%) rename {tests_cupy => test}/test_ccpi_regularisers_cupy.py (100%) rename {tests_cupy => test}/test_data/tomo_standard.npz (100%) diff --git a/Installation.md b/Installation.md index 5f4712d..1747030 100644 --- a/Installation.md +++ b/Installation.md @@ -56,7 +56,7 @@ conda install ccpi-regulariser -c ccpi -c conda-forge #### Python (conda-build) ```sh -conda build recipe/ --numpy 1.23 --python 3.10 +conda build recipe/ --numpy 1.26 --python 3.10 -c conda-forge conda install ccpi-regulariser --use-local --force-reinstall # doesn't work? conda install -c file://${CONDA_PREFIX}/conda-bld/ ccpi-regulariser --force-reinstall # try this one cd demos/ diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 8347dcc..5a72076 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -17,7 +17,7 @@ test: - ./test/ commands: - python -c "import os; print (os.getcwd())" - - python -m unittest discover -s test + - pytest requirements: build: - python @@ -30,6 +30,8 @@ requirements: run: - {{ pin_compatible('numpy', min_pin='x.x', max_pin='x.x') }} - python + - cupy + - pytest - vc 14 # [win] - libgcc-ng # [unix] diff --git a/tests_cupy/conftest.py b/test/conftest.py similarity index 100% rename from tests_cupy/conftest.py rename to test/conftest.py diff --git a/tests_cupy/test_ccpi_regularisers_cupy.py b/test/test_ccpi_regularisers_cupy.py similarity index 100% rename from tests_cupy/test_ccpi_regularisers_cupy.py rename to test/test_ccpi_regularisers_cupy.py diff --git a/tests_cupy/test_data/tomo_standard.npz b/test/test_data/tomo_standard.npz similarity index 100% rename from tests_cupy/test_data/tomo_standard.npz rename to test/test_data/tomo_standard.npz From e3d2a60340e96f3978469db407b41735ef8b26af Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 14:22:40 +0100 Subject: [PATCH 04/15] starting with pytest tests and new test image --- demos/data/lena_gray_512.tif | Bin 262598 -> 0 bytes demos/demo_cpu_regularisers.py | 43 +- pytest.ini | 9 + recipe/meta.yaml | 3 +- test/conftest.py | 144 +++- test/test_2d_cpu_vs_gpu.py | 1233 ++++++++++++++------------- test/test_3d_cpu_vs_gpu.py | 175 ---- test/test_CPU_regularisers.py | 141 --- test/test_ccpi_regularisers_cupy.py | 78 -- test/test_cupy_regul.py | 81 ++ test/test_data/peppers.tif | Bin 0 -> 268900 bytes test/test_imageLena.bin | 1 - test/testroutines.py | 44 - 13 files changed, 872 insertions(+), 1080 deletions(-) delete mode 100644 demos/data/lena_gray_512.tif create mode 100644 pytest.ini delete mode 100644 test/test_3d_cpu_vs_gpu.py delete mode 100644 test/test_CPU_regularisers.py delete mode 100644 test/test_ccpi_regularisers_cupy.py create mode 100644 test/test_cupy_regul.py create mode 100644 test/test_data/peppers.tif delete mode 100644 test/test_imageLena.bin delete mode 100644 test/testroutines.py diff --git a/demos/data/lena_gray_512.tif b/demos/data/lena_gray_512.tif deleted file mode 100644 index f80cafca6db3b6c3cdb684daf381e1d347fede78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262598 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

`Gy7Z*za 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%0DJU;gkjn|=^K_CB=# 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 1N591ezE2| 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 diff --git a/demos/demo_cpu_regularisers.py b/demos/demo_cpu_regularisers.py index f0ffe27..ee4e5ea 100644 --- a/demos/demo_cpu_regularisers.py +++ b/demos/demo_cpu_regularisers.py @@ -12,6 +12,7 @@ import numpy as np import os import timeit +from imageio.v2 import imread from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, TNV, NDF, Diff4th from ccpi.filters.regularisers import PatchSelect, NLTV @@ -32,14 +33,13 @@ def printParametersToString(pars): return txt ############################################################################### -filename = os.path.join( "data" ,"lena_gray_512.tif") +filename = os.path.join( "../test/test_data" ,"peppers.tif") # read image -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') +Im = imread(filename) Im = Im/255.0 -perc = 0.05 +perc = 0.08 u0 = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) @@ -52,24 +52,9 @@ def printParametersToString(pars): u0 = u0.astype('float32') u_ref = u_ref.astype('float32') -# change dims to check that modules work with non-squared images -""" -M = M-100 -u_ref2 = np.zeros([N,M],dtype='float32') -u_ref2[:,0:M] = u_ref[:,0:M] -u_ref = u_ref2 -del u_ref2 - -u02 = np.zeros([N,M],dtype='float32') -u02[:,0:M] = u0[:,0:M] -u0 = u02 -del u02 - -Im2 = np.zeros([N,M],dtype='float32') -Im2[:,0:M] = Im[:,0:M] -Im = Im2 -del Im2 -""" +plt.figure() +plt.imshow(u0, cmap="gray") +plt.show() #%% print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") print ("_______________ROF-TV (2D)_________________") @@ -90,13 +75,14 @@ def printParametersToString(pars): 'time_marching_parameter': 0.001,\ 'tolerance_constant':1e-06} +info_vec = np.zeros(2, dtype = np.float32) print ("#############ROF TV CPU####################") start_time = timeit.default_timer() -(rof_cpu,info_vec_cpu) = ROF_TV(pars['input'], +rof_cpu = ROF_TV(pars['input'], pars['regularisation_parameter'], pars['number_of_iterations'], pars['time_marching_parameter'], - pars['tolerance_constant'], 'cpu') + pars['tolerance_constant'], device = 'cpu', infovector=info_vec) Qtools = QualityTools(Im, rof_cpu) pars['rmse'] = Qtools.rmse() @@ -113,7 +99,6 @@ def printParametersToString(pars): verticalalignment='top', bbox=props) imgplot = plt.imshow(rof_cpu, cmap="gray") plt.title('{}'.format('CPU results')) - #%% print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") print ("_______________FGP-TV (2D)__________________") @@ -137,12 +122,12 @@ def printParametersToString(pars): print ("#############FGP TV CPU####################") start_time = timeit.default_timer() -(fgp_cpu,info_vec_cpu) = FGP_TV(pars['input'], +fgp_cpu = FGP_TV(pars['input'], pars['regularisation_parameter'], pars['number_of_iterations'], pars['tolerance_constant'], pars['methodTV'], - pars['nonneg'],'cpu') + pars['nonneg'], device ='cpu') Qtools = QualityTools(Im, fgp_cpu) pars['rmse'] = Qtools.rmse() @@ -184,13 +169,13 @@ def printParametersToString(pars): print ("#############PD TV CPU####################") start_time = timeit.default_timer() -(pd_cpu,info_vec_cpu) = PD_TV(pars['input'], +pd_cpu = PD_TV(pars['input'], pars['regularisation_parameter'], pars['number_of_iterations'], pars['tolerance_constant'], pars['methodTV'], pars['nonneg'], - pars['lipschitz_const'],'cpu') + pars['lipschitz_const'], device='cpu') Qtools = QualityTools(Im, pd_cpu) pars['rmse'] = Qtools.rmse() diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..aa39b19 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,9 @@ +# pytest.ini +[pytest] +minversion = 6.0 +addopts = -ra -q +testpaths = + test +markers = + cupy: marks tests as cupy (deselect with '-m "not cupy"') + serial diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 5a72076..dcb2530 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -17,7 +17,7 @@ test: - ./test/ commands: - python -c "import os; print (os.getcwd())" - - pytest + - pytest -s requirements: build: - python @@ -30,7 +30,6 @@ requirements: run: - {{ pin_compatible('numpy', min_pin='x.x', max_pin='x.x') }} - python - - cupy - pytest - vc 14 # [win] - libgcc-ng # [unix] diff --git a/test/conftest.py b/test/conftest.py index a4916b6..874185b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,17 +1,67 @@ # Defines common fixtures and makes them available to all tests import os +import subprocess +from imageio.v2 import imread -import cupy as cp import numpy as np import pytest +cupy_enabled = True +try: + import cupy as xp + + try: + xp.cuda.Device(0).compute_capability + + except xp.cuda.runtime.CUDARuntimeError: + import numpy as xp + + cupy_enabled = False + +except ImportError: + + import numpy as xp + + cupy_enabled = False + +# nvidia +try: + subprocess.check_output("nvidia-smi") + has_nvidia = True +except: + has_nvidia = False + CUR_DIR = os.path.abspath(os.path.dirname(__file__)) + +def pytest_addoption(parser): + parser.addoption( + "--runcupy", action="store_true", default=False, help="run cupy tests" + ) + + +def pytest_collection_modifyitems(config, items): + if config.getoption("--runcupy"): + # --runcupy given in cli: do not skip cupy tests + return + skip_cupy = pytest.mark.skip(reason="need --runcupy option to run") + for item in items: + if "cupy" in item.keywords: + item.add_marker(skip_cupy) + + +@pytest.fixture(scope="function", autouse=True) +def my_common_fixture(request): + if not has_nvidia: + pytest.skip("GPU is not available, the test is skipped") + + @pytest.fixture(scope="session") def test_data_path(): return os.path.join(CUR_DIR, "test_data") + # only load from disk once per session, and we use np.copy for the elements, # to ensure data in this loaded file stays as originally loaded @pytest.fixture(scope="session") @@ -20,18 +70,98 @@ def data_file(test_data_path): return np.load(in_file) +@pytest.fixture(scope="session") +def host_pepper_im(test_data_path): + in_file = os.path.join(test_data_path, "peppers.tif") + Im = imread(in_file) + Im = Im / 255 + return Im.astype("float32") + + +@pytest.fixture(scope="session") +def host_pepper_im_nonsquare(test_data_path): + in_file = os.path.join(test_data_path, "peppers.tif") + Im = imread(in_file) + Im_cropped = Im[0:415, 0:460] + Im_cropped = Im_cropped / 255 + return Im_cropped.astype("float32") + + +@pytest.fixture(scope="session") +def host_pepper_im_noise(host_pepper_im): + perc = 0.05 + u0 = host_pepper_im + np.random.normal( + loc=0, scale=perc * host_pepper_im, size=np.shape(host_pepper_im) + ) + u0 = u0.astype("float32") + return u0 + + +@pytest.fixture(scope="session") +def host_pepper_im_noise_nonsquare(host_pepper_im_nonsquare): + perc = 0.05 + u0 = host_pepper_im_nonsquare + np.random.normal( + loc=0, + scale=perc * host_pepper_im_nonsquare, + size=np.shape(host_pepper_im_nonsquare), + ) + u0 = u0.astype("float32") + return u0 + + +@pytest.fixture +def device_pepper_im(host_pepper_im, ensure_clean_memory): + return xp.ascontiguousarray(xp.asarray(host_pepper_im, order="C"), dtype=np.float32) + + +@pytest.fixture +def device_pepper_im_noise(host_pepper_im_noise, ensure_clean_memory): + return xp.ascontiguousarray( + xp.asarray(host_pepper_im_noise, order="C"), dtype=np.float32 + ) + + @pytest.fixture def ensure_clean_memory(): - cp.get_default_memory_pool().free_all_blocks() - cp.get_default_pinned_memory_pool().free_all_blocks() + xp.get_default_memory_pool().free_all_blocks() + xp.get_default_pinned_memory_pool().free_all_blocks() yield None - cp.get_default_memory_pool().free_all_blocks() - cp.get_default_pinned_memory_pool().free_all_blocks() + xp.get_default_memory_pool().free_all_blocks() + xp.get_default_pinned_memory_pool().free_all_blocks() + @pytest.fixture def host_data(data_file): return np.copy(data_file["data"]) + @pytest.fixture -def data(host_data, ensure_clean_memory): - return cp.asarray(host_data) +def device_data(host_data, ensure_clean_memory): + return xp.ascontiguousarray(xp.asarray(host_data, order="C"), dtype=np.float32) + + +def printParametersToString(pars): + txt = r"" + for key, value in pars.items(): + if key == "algorithm": + txt += "{0} = {1}".format(key, value.__name__) + elif key == "input": + txt += "{0} = {1}".format(key, np.shape(value)) + elif key == "refdata": + txt += "{0} = {1}".format(key, np.shape(value)) + else: + txt += "{0} = {1}".format(key, value) + txt += "\n" + return txt + + +def nrmse(im1, im2): + rmse = np.sqrt(np.sum((im2 - im1) ** 2) / float(im1.size)) + max_val = max(np.max(im1), np.max(im2)) + min_val = min(np.min(im1), np.min(im2)) + return 1 - (rmse / (max_val - min_val)) + + +def rmse(im1, im2): + rmse = np.sqrt(np.sum((im1 - im2) ** 2) / float(im1.size)) + return rmse diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index 328db77..404f493 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -1,4 +1,4 @@ -import unittest +import pytest import numpy as np import os import timeit @@ -13,605 +13,632 @@ NDF, Diff4th, ) -from ccpi.filters.utils import cilregcuda - -gpu_modules_available = cilregcuda is not None -from testroutines import BinReader, rmse, printParametersToString - - -@unittest.skipUnless(gpu_modules_available, "Skipping as GPU modules not available") -class TestRegularisers(unittest.TestCase): - def setUp(self): - self.filename = os.path.join(os.path.dirname(__file__), "test_imageLena.bin") - # lena_gray_512.tif - - def _initiate_data(self): - plt = BinReader() - # read image - Im = plt.imread(self.filename) - Im = np.asarray(Im, dtype="float32") - - Im = Im / 255 - perc = 0.05 - u0 = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) - u_ref = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) - - # map the u0 u0->u0>0 - # f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) - u0 = u0.astype("float32") - u_ref = u_ref.astype("float32") - - return u0, u_ref, Im - - def test_ROF_TV_CPU_vs_GPU(self): - - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________ROF-TV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": ROF_TV, - "input": u0, - "regularisation_parameter": 0.02, - "number_of_iterations": 1000, - "time_marching_parameter": 0.001, - "tolerance_constant": 0.0, - } - print("#############ROF TV CPU####################") - start_time = timeit.default_timer() - rof_cpu = ROF_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="cpu", - ) - rms = rmse(Im, rof_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("##############ROF TV GPU##################") - start_time = timeit.default_timer() - rof_gpu = ROF_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="gpu", - ) - - rms = rmse(Im, rof_gpu) - pars["rmse"] = rms - pars["algorithm"] = ROF_TV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(rof_cpu)) - diff_im = abs(rof_cpu - rof_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - def test_FGP_TV_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________FGP-TV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": FGP_TV, - "input": u0, - "regularisation_parameter": 0.02, - "number_of_iterations": 400, - "tolerance_constant": 0.0, - "methodTV": 0, - "nonneg": 0, - } - - print("#############FGP TV CPU####################") - start_time = timeit.default_timer() - fgp_cpu = FGP_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["methodTV"], - pars["nonneg"], - device="cpu", - ) - - rms = rmse(Im, fgp_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("##############FGP TV GPU##################") - start_time = timeit.default_timer() - fgp_gpu = FGP_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["methodTV"], - pars["nonneg"], - device="gpu", - ) - - rms = rmse(Im, fgp_gpu) - pars["rmse"] = rms - pars["algorithm"] = FGP_TV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(fgp_cpu)) - diff_im = abs(fgp_cpu - fgp_gpu) - diff_im[diff_im > tolerance] = 1 - - self.assertLessEqual(diff_im.sum(), 1) - - def test_PD_TV_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________PD-TV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - pars = { - "algorithm": PD_TV, - "input": u0, - "regularisation_parameter": 0.02, - "number_of_iterations": 1500, - "tolerance_constant": 0.0, - "methodTV": 0, - "nonneg": 0, - "lipschitz_const": 8, - } - - print("#############PD TV CPU####################") - start_time = timeit.default_timer() - pd_cpu = PD_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["lipschitz_const"], - pars["methodTV"], - pars["nonneg"], - device="cpu", - ) - - rms = rmse(Im, pd_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("##############PD TV GPU##################") - start_time = timeit.default_timer() - pd_gpu = PD_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["lipschitz_const"], - pars["methodTV"], - pars["nonneg"], - device="gpu", - ) - - rms = rmse(Im, pd_gpu) - pars["rmse"] = rms - pars["algorithm"] = PD_TV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(pd_cpu)) - diff_im = abs(pd_cpu - pd_gpu) - diff_im[diff_im > tolerance] = 1 - - self.assertLessEqual(diff_im.sum(), 1) - - def test_SB_TV_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________SB-TV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": SB_TV, - "input": u0, - "regularisation_parameter": 0.02, - "number_of_iterations": 250, - "tolerance_constant": 0.0, - "methodTV": 0, - } - - print("#############SB-TV CPU####################") - start_time = timeit.default_timer() - sb_cpu = SB_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["methodTV"], - device="cpu", - ) - - rms = rmse(Im, sb_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("##############SB TV GPU##################") - start_time = timeit.default_timer() - sb_gpu = SB_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["methodTV"], - device="gpu", - ) - - rms = rmse(Im, sb_gpu) - pars["rmse"] = rms - pars["algorithm"] = SB_TV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(sb_cpu)) - diff_im = abs(sb_cpu - sb_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - def test_TGV_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________TGV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - # set parameters - pars = { - "algorithm": TGV, - "input": u0, - "regularisation_parameter": 0.02, - "alpha1": 1.0, - "alpha0": 2.0, - "number_of_iterations": 1000, - "LipshitzConstant": 12, - "tolerance_constant": 0.0, - } - - print("#############TGV CPU####################") - start_time = timeit.default_timer() - infovector = np.zeros((2,), dtype="float32") - tgv_cpu = TGV( - pars["input"], - pars["regularisation_parameter"], - pars["alpha1"], - pars["alpha0"], - pars["number_of_iterations"], - pars["LipshitzConstant"], - pars["tolerance_constant"], - device="cpu", - infovector=infovector, - ) - - rms = rmse(Im, tgv_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("##############TGV GPU##################") - start_time = timeit.default_timer() - tgv_gpu = TGV( - pars["input"], - pars["regularisation_parameter"], - pars["alpha1"], - pars["alpha0"], - pars["number_of_iterations"], - pars["LipshitzConstant"], - pars["tolerance_constant"], - device="gpu", - infovector=infovector, - ) - - rms = rmse(Im, tgv_gpu) - pars["rmse"] = rms - pars["algorithm"] = TGV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-02 - diff_im = np.zeros(np.shape(tgv_gpu)) - diff_im = abs(tgv_cpu - tgv_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - def test_LLT_ROF_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________LLT-ROF bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": LLT_ROF, - "input": u0, - "regularisation_parameterROF": 0.01, - "regularisation_parameterLLT": 0.0085, - "number_of_iterations": 1000, - "time_marching_parameter": 0.0001, - "tolerance_constant": 0.0, - } - - print("#############LLT- ROF CPU####################") - start_time = timeit.default_timer() - lltrof_cpu = LLT_ROF( - pars["input"], - pars["regularisation_parameterROF"], - pars["regularisation_parameterLLT"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="cpu", - ) - - rms = rmse(Im, lltrof_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("#############LLT- ROF GPU####################") - start_time = timeit.default_timer() - lltrof_gpu = LLT_ROF( - pars["input"], - pars["regularisation_parameterROF"], - pars["regularisation_parameterLLT"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="gpu", - ) - - rms = rmse(Im, lltrof_gpu) - pars["rmse"] = rms - pars["algorithm"] = LLT_ROF - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(lltrof_gpu)) - diff_im = abs(lltrof_cpu - lltrof_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - def test_NDF_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("_______________NDF bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": NDF, - "input": u0, - "regularisation_parameter": 0.02, - "edge_parameter": 0.017, - "number_of_iterations": 1500, - "time_marching_parameter": 0.01, - "penalty_type": 1, - "tolerance_constant": 0.0, - } - - print("#############NDF CPU####################") - start_time = timeit.default_timer() - ndf_cpu = NDF( - pars["input"], - pars["regularisation_parameter"], - pars["edge_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["penalty_type"], - pars["tolerance_constant"], - device="cpu", - ) - - rms = rmse(Im, ndf_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("##############NDF GPU##################") - start_time = timeit.default_timer() - ndf_gpu = NDF( - pars["input"], - pars["regularisation_parameter"], - pars["edge_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["penalty_type"], - pars["tolerance_constant"], - device="gpu", - ) - - rms = rmse(Im, ndf_gpu) - pars["rmse"] = rms - pars["algorithm"] = NDF - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(ndf_cpu)) - diff_im = abs(ndf_cpu - ndf_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - def test_Diff4th_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("___Anisotropic Diffusion 4th Order (2D)____") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": Diff4th, - "input": u0, - "regularisation_parameter": 0.8, - "edge_parameter": 0.02, - "number_of_iterations": 1000, - "time_marching_parameter": 0.0001, - "tolerance_constant": 0.0, - } - - print("#############Diff4th CPU####################") - start_time = timeit.default_timer() - diff4th_cpu = Diff4th( - pars["input"], - pars["regularisation_parameter"], - pars["edge_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="cpu", - ) - - rms = rmse(Im, diff4th_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("##############Diff4th GPU##################") - start_time = timeit.default_timer() - diff4th_gpu = Diff4th( - pars["input"], - pars["regularisation_parameter"], - pars["edge_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="gpu", - ) - - rms = rmse(Im, diff4th_gpu) - pars["rmse"] = rms - pars["algorithm"] = Diff4th - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(diff4th_cpu)) - diff_im = abs(diff4th_cpu - diff4th_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - def test_FDGdTV_CPU_vs_GPU(self): - u0, u_ref, Im = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________FGP-dTV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": FGP_dTV, - "input": u0, - "refdata": u_ref, - "regularisation_parameter": 0.02, - "number_of_iterations": 500, - "tolerance_constant": 0.0, - "eta_const": 0.2, - "methodTV": 0, - "nonneg": 0, - } - - print("#############FGP dTV CPU####################") - start_time = timeit.default_timer() - fgp_dtv_cpu = FGP_dTV( - pars["input"], - pars["refdata"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["eta_const"], - pars["methodTV"], - pars["nonneg"], - device="cpu", - ) - - rms = rmse(Im, fgp_dtv_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("##############FGP dTV GPU##################") - start_time = timeit.default_timer() - fgp_dtv_gpu = FGP_dTV( - pars["input"], - pars["refdata"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["eta_const"], - pars["methodTV"], - pars["nonneg"], - device="gpu", - ) - - rms = rmse(Im, fgp_dtv_gpu) - pars["rmse"] = rms - pars["algorithm"] = FGP_dTV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(fgp_dtv_cpu)) - diff_im = abs(fgp_dtv_cpu - fgp_dtv_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - -if __name__ == "__main__": - unittest.main() +from conftest import rmse, printParametersToString +from numpy.testing import assert_allclose + + +def test_ROF_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + # set parameters + pars = { + "algorithm": ROF_TV, + "input": host_pepper_im_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 1000, + "time_marching_parameter": 0.001, + "tolerance_constant": 0.0, + } + print("#############ROF TV CPU####################") + rof_cpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, rof_cpu) + print("##############ROF TV GPU##################") + rof_gpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, rof_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert rof_cpu.dtype == np.float32 + assert rof_gpu.dtype == np.float32 + + +def test_ROF_TV_CPU_vs_GPU_nonsquare( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + # set parameters + pars = { + "algorithm": ROF_TV, + "input": host_pepper_im_noise_nonsquare, + "regularisation_parameter": 0.02, + "number_of_iterations": 1000, + "time_marching_parameter": 0.001, + "tolerance_constant": 0.0, + } + print("#############ROF TV CPU####################") + rof_cpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, rof_cpu) + print("##############ROF TV GPU##################") + rof_gpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, rof_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert rof_cpu.dtype == np.float32 + assert rof_gpu.dtype == np.float32 + + +def test_FGP_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + pars = { + "algorithm": FGP_TV, + "input": host_pepper_im_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 400, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + } + + print("#############FGP TV CPU####################") + fgp_cpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, fgp_cpu) + print("##############FGP TV GPU##################") + fgp_gpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, fgp_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_cpu.dtype == np.float32 + assert fgp_gpu.dtype == np.float32 + + +def test_FGP_TV_CPU_vs_GPU_nonsquare( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + pars = { + "algorithm": FGP_TV, + "input": host_pepper_im_noise_nonsquare, + "regularisation_parameter": 0.02, + "number_of_iterations": 400, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + } + + print("#############FGP TV CPU####################") + fgp_cpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, fgp_cpu) + print("##############FGP TV GPU##################") + fgp_gpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, fgp_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_cpu.dtype == np.float32 + assert fgp_gpu.dtype == np.float32 + + +# def test_PD_TV_CPU_vs_GPU(self): +# u0, u_ref, Im = self._initiate_data() + +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# print("____________PD-TV bench___________________") +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +# pars = { +# "algorithm": PD_TV, +# "input": u0, +# "regularisation_parameter": 0.02, +# "number_of_iterations": 1500, +# "tolerance_constant": 0.0, +# "methodTV": 0, +# "nonneg": 0, +# "lipschitz_const": 8, +# } + +# print("#############PD TV CPU####################") +# start_time = timeit.default_timer() +# pd_cpu = PD_TV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["number_of_iterations"], +# pars["tolerance_constant"], +# pars["lipschitz_const"], +# pars["methodTV"], +# pars["nonneg"], +# device="cpu", +# ) + +# rms = rmse(Im, pd_cpu) +# pars["rmse"] = rms + +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) + +# print("##############PD TV GPU##################") +# start_time = timeit.default_timer() +# pd_gpu = PD_TV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["number_of_iterations"], +# pars["tolerance_constant"], +# pars["lipschitz_const"], +# pars["methodTV"], +# pars["nonneg"], +# device="gpu", +# ) + +# rms = rmse(Im, pd_gpu) +# pars["rmse"] = rms +# pars["algorithm"] = PD_TV +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) + +# print("--------Compare the results--------") +# tolerance = 1e-05 +# diff_im = np.zeros(np.shape(pd_cpu)) +# diff_im = abs(pd_cpu - pd_gpu) +# diff_im[diff_im > tolerance] = 1 + +# self.assertLessEqual(diff_im.sum(), 1) + +# def test_SB_TV_CPU_vs_GPU(self): +# u0, u_ref, Im = self._initiate_data() + +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# print("____________SB-TV bench___________________") +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +# # set parameters +# pars = { +# "algorithm": SB_TV, +# "input": u0, +# "regularisation_parameter": 0.02, +# "number_of_iterations": 250, +# "tolerance_constant": 0.0, +# "methodTV": 0, +# } + +# print("#############SB-TV CPU####################") +# start_time = timeit.default_timer() +# sb_cpu = SB_TV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["number_of_iterations"], +# pars["tolerance_constant"], +# pars["methodTV"], +# device="cpu", +# ) + +# rms = rmse(Im, sb_cpu) +# pars["rmse"] = rms + +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) + +# print("##############SB TV GPU##################") +# start_time = timeit.default_timer() +# sb_gpu = SB_TV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["number_of_iterations"], +# pars["tolerance_constant"], +# pars["methodTV"], +# device="gpu", +# ) + +# rms = rmse(Im, sb_gpu) +# pars["rmse"] = rms +# pars["algorithm"] = SB_TV +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("--------Compare the results--------") +# tolerance = 1e-05 +# diff_im = np.zeros(np.shape(sb_cpu)) +# diff_im = abs(sb_cpu - sb_gpu) +# diff_im[diff_im > tolerance] = 1 +# self.assertLessEqual(diff_im.sum(), 1) + +# def test_TGV_CPU_vs_GPU(self): +# u0, u_ref, Im = self._initiate_data() + +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# print("____________TGV bench___________________") +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +# # set parameters +# # set parameters +# pars = { +# "algorithm": TGV, +# "input": u0, +# "regularisation_parameter": 0.02, +# "alpha1": 1.0, +# "alpha0": 2.0, +# "number_of_iterations": 1000, +# "LipshitzConstant": 12, +# "tolerance_constant": 0.0, +# } + +# print("#############TGV CPU####################") +# start_time = timeit.default_timer() +# infovector = np.zeros((2,), dtype="float32") +# tgv_cpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="cpu", +# infovector=infovector, +# ) + +# rms = rmse(Im, tgv_cpu) +# pars["rmse"] = rms + +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) + +# print("##############TGV GPU##################") +# start_time = timeit.default_timer() +# tgv_gpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="gpu", +# infovector=infovector, +# ) + +# rms = rmse(Im, tgv_gpu) +# pars["rmse"] = rms +# pars["algorithm"] = TGV +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("--------Compare the results--------") +# tolerance = 1e-02 +# diff_im = np.zeros(np.shape(tgv_gpu)) +# diff_im = abs(tgv_cpu - tgv_gpu) +# diff_im[diff_im > tolerance] = 1 +# self.assertLessEqual(diff_im.sum(), 1) + +# def test_LLT_ROF_CPU_vs_GPU(self): +# u0, u_ref, Im = self._initiate_data() + +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# print("____________LLT-ROF bench___________________") +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +# # set parameters +# pars = { +# "algorithm": LLT_ROF, +# "input": u0, +# "regularisation_parameterROF": 0.01, +# "regularisation_parameterLLT": 0.0085, +# "number_of_iterations": 1000, +# "time_marching_parameter": 0.0001, +# "tolerance_constant": 0.0, +# } + +# print("#############LLT- ROF CPU####################") +# start_time = timeit.default_timer() +# lltrof_cpu = LLT_ROF( +# pars["input"], +# pars["regularisation_parameterROF"], +# pars["regularisation_parameterLLT"], +# pars["number_of_iterations"], +# pars["time_marching_parameter"], +# pars["tolerance_constant"], +# device="cpu", +# ) + +# rms = rmse(Im, lltrof_cpu) +# pars["rmse"] = rms + +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("#############LLT- ROF GPU####################") +# start_time = timeit.default_timer() +# lltrof_gpu = LLT_ROF( +# pars["input"], +# pars["regularisation_parameterROF"], +# pars["regularisation_parameterLLT"], +# pars["number_of_iterations"], +# pars["time_marching_parameter"], +# pars["tolerance_constant"], +# device="gpu", +# ) + +# rms = rmse(Im, lltrof_gpu) +# pars["rmse"] = rms +# pars["algorithm"] = LLT_ROF +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("--------Compare the results--------") +# tolerance = 1e-05 +# diff_im = np.zeros(np.shape(lltrof_gpu)) +# diff_im = abs(lltrof_cpu - lltrof_gpu) +# diff_im[diff_im > tolerance] = 1 +# self.assertLessEqual(diff_im.sum(), 1) + +# def test_NDF_CPU_vs_GPU(self): +# u0, u_ref, Im = self._initiate_data() + +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# print("_______________NDF bench___________________") +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +# # set parameters +# pars = { +# "algorithm": NDF, +# "input": u0, +# "regularisation_parameter": 0.02, +# "edge_parameter": 0.017, +# "number_of_iterations": 1500, +# "time_marching_parameter": 0.01, +# "penalty_type": 1, +# "tolerance_constant": 0.0, +# } + +# print("#############NDF CPU####################") +# start_time = timeit.default_timer() +# ndf_cpu = NDF( +# pars["input"], +# pars["regularisation_parameter"], +# pars["edge_parameter"], +# pars["number_of_iterations"], +# pars["time_marching_parameter"], +# pars["penalty_type"], +# pars["tolerance_constant"], +# device="cpu", +# ) + +# rms = rmse(Im, ndf_cpu) +# pars["rmse"] = rms + +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) + +# print("##############NDF GPU##################") +# start_time = timeit.default_timer() +# ndf_gpu = NDF( +# pars["input"], +# pars["regularisation_parameter"], +# pars["edge_parameter"], +# pars["number_of_iterations"], +# pars["time_marching_parameter"], +# pars["penalty_type"], +# pars["tolerance_constant"], +# device="gpu", +# ) + +# rms = rmse(Im, ndf_gpu) +# pars["rmse"] = rms +# pars["algorithm"] = NDF +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("--------Compare the results--------") +# tolerance = 1e-05 +# diff_im = np.zeros(np.shape(ndf_cpu)) +# diff_im = abs(ndf_cpu - ndf_gpu) +# diff_im[diff_im > tolerance] = 1 +# self.assertLessEqual(diff_im.sum(), 1) + +# def test_Diff4th_CPU_vs_GPU(self): +# u0, u_ref, Im = self._initiate_data() + +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# print("___Anisotropic Diffusion 4th Order (2D)____") +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +# # set parameters +# pars = { +# "algorithm": Diff4th, +# "input": u0, +# "regularisation_parameter": 0.8, +# "edge_parameter": 0.02, +# "number_of_iterations": 1000, +# "time_marching_parameter": 0.0001, +# "tolerance_constant": 0.0, +# } + +# print("#############Diff4th CPU####################") +# start_time = timeit.default_timer() +# diff4th_cpu = Diff4th( +# pars["input"], +# pars["regularisation_parameter"], +# pars["edge_parameter"], +# pars["number_of_iterations"], +# pars["time_marching_parameter"], +# pars["tolerance_constant"], +# device="cpu", +# ) + +# rms = rmse(Im, diff4th_cpu) +# pars["rmse"] = rms + +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("##############Diff4th GPU##################") +# start_time = timeit.default_timer() +# diff4th_gpu = Diff4th( +# pars["input"], +# pars["regularisation_parameter"], +# pars["edge_parameter"], +# pars["number_of_iterations"], +# pars["time_marching_parameter"], +# pars["tolerance_constant"], +# device="gpu", +# ) + +# rms = rmse(Im, diff4th_gpu) +# pars["rmse"] = rms +# pars["algorithm"] = Diff4th +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("--------Compare the results--------") +# tolerance = 1e-05 +# diff_im = np.zeros(np.shape(diff4th_cpu)) +# diff_im = abs(diff4th_cpu - diff4th_gpu) +# diff_im[diff_im > tolerance] = 1 +# self.assertLessEqual(diff_im.sum(), 1) + +# def test_FDGdTV_CPU_vs_GPU(self): +# u0, u_ref, Im = self._initiate_data() + +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# print("____________FGP-dTV bench___________________") +# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +# # set parameters +# pars = { +# "algorithm": FGP_dTV, +# "input": u0, +# "refdata": u_ref, +# "regularisation_parameter": 0.02, +# "number_of_iterations": 500, +# "tolerance_constant": 0.0, +# "eta_const": 0.2, +# "methodTV": 0, +# "nonneg": 0, +# } + +# print("#############FGP dTV CPU####################") +# start_time = timeit.default_timer() +# fgp_dtv_cpu = FGP_dTV( +# pars["input"], +# pars["refdata"], +# pars["regularisation_parameter"], +# pars["number_of_iterations"], +# pars["tolerance_constant"], +# pars["eta_const"], +# pars["methodTV"], +# pars["nonneg"], +# device="cpu", +# ) + +# rms = rmse(Im, fgp_dtv_cpu) +# pars["rmse"] = rms + +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("##############FGP dTV GPU##################") +# start_time = timeit.default_timer() +# fgp_dtv_gpu = FGP_dTV( +# pars["input"], +# pars["refdata"], +# pars["regularisation_parameter"], +# pars["number_of_iterations"], +# pars["tolerance_constant"], +# pars["eta_const"], +# pars["methodTV"], +# pars["nonneg"], +# device="gpu", +# ) + +# rms = rmse(Im, fgp_dtv_gpu) +# pars["rmse"] = rms +# pars["algorithm"] = FGP_dTV +# txtstr = printParametersToString(pars) +# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +# print(txtstr) +# print("--------Compare the results--------") +# tolerance = 1e-05 +# diff_im = np.zeros(np.shape(fgp_dtv_cpu)) +# diff_im = abs(fgp_dtv_cpu - fgp_dtv_gpu) +# diff_im[diff_im > tolerance] = 1 +# self.assertLessEqual(diff_im.sum(), 1) + + +# if __name__ == "__main__": +# unittest.main() diff --git a/test/test_3d_cpu_vs_gpu.py b/test/test_3d_cpu_vs_gpu.py deleted file mode 100644 index 30ba582..0000000 --- a/test/test_3d_cpu_vs_gpu.py +++ /dev/null @@ -1,175 +0,0 @@ -import unittest -import numpy as np -import os -import timeit -from ccpi.filters.regularisers import ( - ROF_TV, - FGP_TV, - PD_TV, - SB_TV, - TGV, - LLT_ROF, - FGP_dTV, - NDF, - Diff4th, -) -from ccpi.filters.utils import cilregcuda - -gpu_modules_available = cilregcuda is not None -from testroutines import BinReader, rmse, printParametersToString - - -@unittest.skipUnless(gpu_modules_available, "Skipping as GPU modules not available") -class TestRegularisers(unittest.TestCase): - def setUp(self): - self.filename = os.path.join(os.path.dirname(__file__), "test_imageLena.bin") - # lena_gray_512.tif - - def _initiate_data(self): - plt = BinReader() - # read image - Im = plt.imread(self.filename) - Im = np.asarray(Im, dtype="float32") - - Im = Im / 255 - perc = 0.05 - slices = 20 - - noisyVol = np.zeros((slices, 512, 512), dtype="float32") - noisyRef = np.zeros((slices, 512, 512), dtype="float32") - idealVol = np.zeros((slices, 512, 512), dtype="float32") - - for i in range(slices): - noisyVol[i, :, :] = Im + np.random.normal( - loc=0, scale=perc * Im, size=np.shape(Im) - ) - noisyRef[i, :, :] = Im + np.random.normal( - loc=0, scale=0.01 * Im, size=np.shape(Im) - ) - idealVol[i, :, :] = Im - return noisyVol, noisyRef, idealVol - - def test_ROF_TV_CPU_vs_GPU(self): - - noisyVol, noisyRef, idealVol = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________ROF-TV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": ROF_TV, - "input": noisyVol, - "regularisation_parameter": 0.02, - "number_of_iterations": 100, - "time_marching_parameter": 0.001, - "tolerance_constant": 0.0, - } - print("#############ROF TV CPU####################") - start_time = timeit.default_timer() - rof_cpu = ROF_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="cpu", - ) - rms = rmse(idealVol, rof_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("##############ROF TV GPU##################") - start_time = timeit.default_timer() - rof_gpu = ROF_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["time_marching_parameter"], - pars["tolerance_constant"], - device="gpu", - ) - - rms = rmse(idealVol, rof_gpu) - pars["rmse"] = rms - pars["algorithm"] = ROF_TV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(rof_cpu)) - diff_im = abs(rof_cpu - rof_gpu) - diff_im[diff_im > tolerance] = 1 - self.assertLessEqual(diff_im.sum(), 1) - - def test_FGP_TV_CPU_vs_GPU(self): - noisyVol, noisyRef, idealVol = self._initiate_data() - - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - print("____________FGP-TV bench___________________") - print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - - # set parameters - pars = { - "algorithm": FGP_TV, - "input": noisyVol, - "regularisation_parameter": 0.05, - "number_of_iterations": 200, - "tolerance_constant": 0.0, - "methodTV": 0, - "nonneg": 0, - } - - print("#############FGP TV CPU####################") - start_time = timeit.default_timer() - fgp_cpu = FGP_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["methodTV"], - pars["nonneg"], - device="cpu", - ) - - rms = rmse(idealVol, fgp_cpu) - pars["rmse"] = rms - - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("##############FGP TV GPU##################") - start_time = timeit.default_timer() - fgp_gpu = FGP_TV( - pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["methodTV"], - pars["nonneg"], - device="gpu", - ) - - rms = rmse(idealVol, fgp_gpu) - pars["rmse"] = rms - pars["algorithm"] = FGP_TV - txtstr = printParametersToString(pars) - txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) - print(txtstr) - - print("--------Compare the results--------") - tolerance = 1e-05 - diff_im = np.zeros(np.shape(fgp_cpu)) - diff_im = abs(fgp_cpu - fgp_gpu) - diff_im[diff_im > tolerance] = 1 - - self.assertLessEqual(diff_im.sum(), 1) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_CPU_regularisers.py b/test/test_CPU_regularisers.py deleted file mode 100644 index 725c80c..0000000 --- a/test/test_CPU_regularisers.py +++ /dev/null @@ -1,141 +0,0 @@ -import unittest -#import math -import os -#import timeit -import numpy as np -from ccpi.filters.regularisers import FGP_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th, ROF_TV, PD_TV -from testroutines import BinReader, rmse -############################################################################### - -class TestRegularisers(unittest.TestCase): - def setUp(self): - self.filename = os.path.join(os.path.dirname(__file__), "test_imageLena.bin") - # lena_gray_512.tif - - def getPars(self): - #plt = TiffReader() - plt = BinReader() - # read image - Im = plt.imread(self.filename) - Im = np.asarray(Im, dtype='float32') - Im = Im / 255 - perc = 0.05 - u0 = Im + np.random.normal(loc=0, - scale=perc * Im, - size=np.shape(Im)) - u_ref = Im + np.random.normal(loc=0, - scale=0.01 * Im, - size=np.shape(Im)) - u0 = u0.astype('float32') - u_ref = u_ref.astype('float32') - return Im,u0,u_ref - - - def test_FGP_TV_CPU(self): - Im,input,ref = self.getPars() - - info = np.zeros((2,), dtype='float32') - - fgp_cpu = FGP_TV(input,0.02,300,0.0,0,0,device='cpu', infovector=info) - - rms = rmse(Im, fgp_cpu) - - self.assertAlmostEqual(rms,0.02,delta=0.01) - - def test_PD_TV_CPU(self): - Im,input,ref = self.getPars() - - info = np.zeros((2,), dtype='float32') - - pd_cpu = PD_TV(input, 0.02, 300, 0.0, 0.1, 0, 1, device='cpu', infovector=info) - - rms = rmse(Im, pd_cpu) - - self.assertAlmostEqual(rms,0.02,delta=0.01) - - def test_TV_ROF_CPU(self): - # set parameters - Im, input,ref = self.getPars() - info = np.zeros((2,), dtype='float32') - # call routine - fgp_cpu = ROF_TV(input,0.02,1000,0.001,0.0, device='cpu', infovector=info) - - rms = rmse(Im, fgp_cpu) - - # now test that it generates some expected output - self.assertAlmostEqual(rms,0.02,delta=0.01) - - def test_SB_TV_CPU(self): - # set parameters - Im, input,ref = self.getPars() - info = np.zeros((2,), dtype='float32') - # call routine - sb_cpu = SB_TV(input,0.02,150,0.0,0,device='cpu', infovector=info) - - rms = rmse(Im, sb_cpu) - - # now test that it generates some expected output - self.assertAlmostEqual(rms,0.02,delta=0.01) - - def test_TGV_CPU(self): - # set parameters - Im, input,ref = self.getPars() - info = np.zeros((2,), dtype='float32') - # call routine - tgv_cpu = TGV(input,0.02,1.0,2.0,500,12,0.0,device='cpu', infovector=info) - - rms = rmse(Im, tgv_cpu) - - # now test that it generates some expected output - self.assertAlmostEqual(rms,0.02,delta=0.01) - - def test_LLT_ROF_CPU(self): - # set parameters - Im, input,ref = self.getPars() - info = np.zeros((2,), dtype='float32') - # call routine - sb_cpu = LLT_ROF(input,0.01,0.008,1000,0.001,0.0,device='cpu', infovector=info) - - rms = rmse(Im, sb_cpu) - - # now test that it generates some expected output - self.assertAlmostEqual(rms,0.02,delta=0.01) - - def test_NDF_CPU(self): - # set parameters - Im, input,ref = self.getPars() - info = np.zeros((2,), dtype='float32') - # call routine - sb_cpu = NDF(input, 0.02, 0.17,1000,0.01,1,0.0, device='cpu', infovector=info) - - rms = rmse(Im, sb_cpu) - - # now test that it generates some expected output - self.assertAlmostEqual(rms, 0.02, delta=0.01) - - def test_Diff4th_CPU(self): - # set parameters - Im, input,ref = self.getPars() - info = np.zeros((2,), dtype='float32') - # call routine - sb_cpu = Diff4th(input, 0.8,0.02,1000,0.001,0.0, device='cpu', infovector=info) - - rms = rmse(Im, sb_cpu) - - # now test that it generates some expected output - self.assertAlmostEqual(rms, 0.02, delta=0.01) - - def test_FGP_dTV_CPU(self): - # set parameters - Im, input,ref = self.getPars() - info = np.zeros((2,), dtype='float32') - # call routine - sb_cpu = FGP_dTV(input,ref,0.02,500,0.0,0.2,0,0, device='cpu', infovector=info) - - rms = rmse(Im, sb_cpu) - - # now test that it generates some expected output - self.assertAlmostEqual(rms, 0.02, delta=0.01) - -if __name__ == '__main__': - unittest.main() diff --git a/test/test_ccpi_regularisers_cupy.py b/test/test_ccpi_regularisers_cupy.py deleted file mode 100644 index fc2c4fd..0000000 --- a/test/test_ccpi_regularisers_cupy.py +++ /dev/null @@ -1,78 +0,0 @@ -import time -import numpy as np -import cupy as cp -import pytest -from ccpi.filters.regularisersCuPy import ROF_TV, PD_TV - -from numpy.testing import assert_allclose, assert_equal - -eps = 1e-5 -def test_ROF_TV_2d(host_data): - cp.get_default_memory_pool().free_all_blocks() - input_np = np.float32(host_data[60,:,:]) - input_cp = cp.asarray(input_np, order="C") - filtered_data = ROF_TV(input_cp, - regularisation_parameter=0.06, - iterations = 1000, - time_marching_parameter=0.001, - gpu_id = 0) - filtered_data = filtered_data.get() - - # comparison to the CUDA implemented output - assert_allclose(np.sum(filtered_data), 16589834.0, rtol=eps) - assert_allclose(np.median(filtered_data), 960.0731, rtol=eps) - assert filtered_data.dtype == np.float32 - -def test_ROF_TV_3d(host_data): - cp.get_default_memory_pool().free_all_blocks() - input_np = np.float32(host_data) - input_cp = cp.asarray(input_np, order="C") - filtered_data = ROF_TV(input_cp, - regularisation_parameter=0.06, - iterations = 1000, - time_marching_parameter=0.001, - gpu_id = 0) - filtered_data = filtered_data.get() - - # comparison to the CUDA implemented output - assert_allclose(np.sum(filtered_data), 2982482200.0, rtol=eps) - assert_allclose(np.median(filtered_data), 960.1991, rtol=eps) - assert filtered_data.dtype == np.float32 - -def test_PD_TV_2d(host_data): - cp.get_default_memory_pool().free_all_blocks() - input_np = np.float32(host_data[60,:,:]) - input_cp = cp.asarray(input_np, order="C") - filtered_data = PD_TV(input_cp, - regularisation_parameter=0.06, - iterations = 1000, - tolerance_param = 0.0, - methodTV=0, - nonneg=0, - lipschitz_const=8, - gpu_id = 0) - filtered_data = filtered_data.get() - - # comparison to the CUDA implemented output - assert_allclose(np.sum(filtered_data), 16589830.0, rtol=eps) - assert_allclose(np.median(filtered_data), 960.11084, rtol=eps) - assert filtered_data.dtype == np.float32 - -def test_PD_TV_3d(host_data): - cp.get_default_memory_pool().free_all_blocks() - input_np = np.float32(host_data) - input_cp = cp.asarray(input_np, order="C") - filtered_data = PD_TV(input_cp, - regularisation_parameter=0.06, - iterations = 1000, - tolerance_param = 0.0, - methodTV=0, - nonneg=0, - lipschitz_const=8, - gpu_id = 0) - filtered_data = filtered_data.get() - - # comparison to the CUDA implemented output - assert_allclose(np.sum(filtered_data), 2982481000.0, rtol=eps) - assert_allclose(np.median(filtered_data), 960.1847, rtol=eps) - assert filtered_data.dtype == np.float32 \ No newline at end of file diff --git a/test/test_cupy_regul.py b/test/test_cupy_regul.py new file mode 100644 index 0000000..5372674 --- /dev/null +++ b/test/test_cupy_regul.py @@ -0,0 +1,81 @@ +import numpy as np +import pytest + +from ccpi.filters.regularisersCuPy import ROF_TV, PD_TV +from numpy.testing import assert_allclose, assert_equal + +eps = 1e-5 + + +@pytest.mark.cupy +def test_ROF_TV_2d(device_data): + filtered_data = ROF_TV( + device_data[60, :, :], + regularisation_parameter=0.06, + iterations=1000, + time_marching_parameter=0.001, + gpu_id=0, + ) + filtered_data = filtered_data.get() + + # comparison to the CUDA implemented output + assert_allclose(np.sum(filtered_data), 16589834.0, rtol=eps) + assert_allclose(np.median(filtered_data), 960.0731, rtol=eps) + assert filtered_data.dtype == np.float32 + + +@pytest.mark.cupy +def test_ROF_TV_3d(device_data): + filtered_data = ROF_TV( + device_data, + regularisation_parameter=0.06, + iterations=1000, + time_marching_parameter=0.001, + gpu_id=0, + ) + filtered_data = filtered_data.get() + + # comparison to the CUDA implemented output + assert_allclose(np.sum(filtered_data), 2982482200.0, rtol=eps) + assert_allclose(np.median(filtered_data), 960.1991, rtol=eps) + assert filtered_data.dtype == np.float32 + + +@pytest.mark.cupy +def test_PD_TV_2d(device_data): + filtered_data = PD_TV( + device_data[60, :, :], + regularisation_parameter=0.06, + iterations=1000, + tolerance_param=0.0, + methodTV=0, + nonneg=0, + lipschitz_const=8, + gpu_id=0, + ) + filtered_data = filtered_data.get() + + # comparison to the CUDA implemented output + assert_allclose(np.sum(filtered_data), 16589830.0, rtol=eps) + assert_allclose(np.median(filtered_data), 960.11084, rtol=eps) + assert filtered_data.dtype == np.float32 + + +@pytest.mark.cupy +def test_PD_TV_3d(device_data): + filtered_data = PD_TV( + device_data, + regularisation_parameter=0.06, + iterations=1000, + tolerance_param=0.0, + methodTV=0, + nonneg=0, + lipschitz_const=8, + gpu_id=0, + ) + filtered_data = filtered_data.get() + + # comparison to the CUDA implemented output + assert_allclose(np.sum(filtered_data), 2982481000.0, rtol=eps) + assert_allclose(np.median(filtered_data), 960.1847, rtol=eps) + assert filtered_data.dtype == np.float32 diff --git a/test/test_data/peppers.tif b/test/test_data/peppers.tif new file mode 100644 index 0000000000000000000000000000000000000000..71576b163e9af2051162eaf8e676a617251cc95c GIT binary patch literal 268900 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

_tkyHz55 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 CE!`^s literal 0 HcmV?d00001 diff --git a/test/test_imageLena.bin b/test/test_imageLena.bin deleted file mode 100644 index 9f5c368..0000000 --- a/test/test_imageLena.bin +++ /dev/null @@ -1 +0,0 @@ -¢¢¢¡¢£¡¥¡¢ ›£ ›œ¡¡šœš››˜œššœšž› ž§ ¦¦¥¦¬«¯­ª¬¬§¯¨§¢¡””›‚wvjaa^\Xaf`gdhiiihnnlkihmmmlkklnmkmmnnlhjjlmounqvywpzyz}zy||‚{z„ƒ„‡€ƒƒ‡ˆ„†ƒƒ€‚†~~…‚ƒ€„€‡…ˆ†€†‚‡……†‡„‡ƒˆ†„……ˆ…††„…ƒ„…‡‰‡…ƒ…ƒ‡ˆ„‡ˆ‰…Šˆ†…‡‡ˆ„…Œ„ƒ‹†ƒ„„ƒ„„ƒ„ƒ€‡……‡ƒ„„„ƒ…‚ƒƒ…ˆ†„‡„‡†Š„„…†…ŠŠŽ†„„€€„†ƒ†…ƒ€ƒ„~‚‡†ƒ~ƒƒ†~„ƒ€ƒƒ}‚}„~y||ywzywuspqsifghptvz„‡‹Œ’–™˜™ŸŸŸ£ž¢¡››”–™–š——›—›˜œššŸž›š™—™—š˜š™˜™™——žšš˜˜™šžžššœŸŸŸœœ˜››ž±ÀÆÎÐÕÔÕØÚÚÚÚÜØÓÌÀ¬˜zjieghoiqtuwuwvy{ywux}yv{z}€yw{vx{{wzyy{z|v|~|~~{||}vv€z~~~‚|~}y|~w{rytwvsx|Ф¨ª«ª›€¢¢¢¡¢£¡¥¡¢ ›£ ›œ¡¡šœš››˜œššœšž› ž§ ¦¦¥¦¬«¯­ª¬¬§¯¨§¢¡””›‚wvjaa^\Xaf`gdhiiihnnlkihmmmlkklnmkmmnnlhjjlmounqvywpzyz}zy||‚{z„ƒ„‡€ƒƒ‡ˆ„†ƒƒ€‚†~~…‚ƒ€„€‡…ˆ†€†‚‡……†‡„‡ƒˆ†„……ˆ…††„…ƒ„…‡‰‡…ƒ…ƒ‡ˆ„‡ˆ‰…Šˆ†…‡‡ˆ„…Œ„ƒ‹†ƒ„„ƒ„„ƒ„ƒ€‡……‡ƒ„„„ƒ…‚ƒƒ…ˆ†„‡„‡†Š„„…†…ŠŠŽ†„„€€„†ƒ†…ƒ€ƒ„~‚‡†ƒ~ƒƒ†~„ƒ€ƒƒ}‚}„~y||ywzywuspqsifghptvz„‡‹Œ’–™˜™ŸŸŸ£ž¢¡››”–™–š——›—›˜œššŸž›š™—™—š˜š™˜™™——žšš˜˜™šžžššœŸŸŸœœ˜››ž±ÀÆÎÐÕÔÕØÚÚÚÚÜØÓÌÀ¬˜zjieghoiqtuwuwvy{ywux}yv{z}€yw{vx{{wzyy{z|v|~|~~{||}vv€z~~~‚|~}y|~w{rytwvsx|Ф¨ª«ª›€¢¢¢¡¢£¡¥¡¢ ›£ ›œ¡¡šœš››˜œššœšž› ž§ ¦¦¥¦¬«¯­ª¬¬§¯¨§¢¡””›‚wvjaa^\Xaf`gdhiiihnnlkihmmmlkklnmkmmnnlhjjlmounqvywpzyz}zy||‚{z„ƒ„‡€ƒƒ‡ˆ„†ƒƒ€‚†~~…‚ƒ€„€‡…ˆ†€†‚‡……†‡„‡ƒˆ†„……ˆ…††„…ƒ„…‡‰‡…ƒ…ƒ‡ˆ„‡ˆ‰…Šˆ†…‡‡ˆ„…Œ„ƒ‹†ƒ„„ƒ„„ƒ„ƒ€‡……‡ƒ„„„ƒ…‚ƒƒ…ˆ†„‡„‡†Š„„…†…ŠŠŽ†„„€€„†ƒ†…ƒ€ƒ„~‚‡†ƒ~ƒƒ†~„ƒ€ƒƒ}‚}„~y||ywzywuspqsifghptvz„‡‹Œ’–™˜™ŸŸŸ£ž¢¡››”–™–š——›—›˜œššŸž›š™—™—š˜š™˜™™——žšš˜˜™šžžššœŸŸŸœœ˜››ž±ÀÆÎÐÕÔÕØÚÚÚÚÜØÓÌÀ¬˜zjieghoiqtuwuwvy{ywux}yv{z}€yw{vx{{wzyy{z|v|~|~~{||}vv€z~~~‚|~}y|~w{rytwvsx|Ф¨ª«ª›€¢¢¢¡¢£¡¥¡¢ ›£ ›œ¡¡šœš››˜œššœšž› ž§ ¦¦¥¦¬«¯­ª¬¬§¯¨§¢¡””›‚wvjaa^\Xaf`gdhiiihnnlkihmmmlkklnmkmmnnlhjjlmounqvywpzyz}zy||‚{z„ƒ„‡€ƒƒ‡ˆ„†ƒƒ€‚†~~…‚ƒ€„€‡…ˆ†€†‚‡……†‡„‡ƒˆ†„……ˆ…††„…ƒ„…‡‰‡…ƒ…ƒ‡ˆ„‡ˆ‰…Šˆ†…‡‡ˆ„…Œ„ƒ‹†ƒ„„ƒ„„ƒ„ƒ€‡……‡ƒ„„„ƒ…‚ƒƒ…ˆ†„‡„‡†Š„„…†…ŠŠŽ†„„€€„†ƒ†…ƒ€ƒ„~‚‡†ƒ~ƒƒ†~„ƒ€ƒƒ}‚}„~y||ywzywuspqsifghptvz„‡‹Œ’–™˜™ŸŸŸ£ž¢¡››”–™–š——›—›˜œššŸž›š™—™—š˜š™˜™™——žšš˜˜™šžžššœŸŸŸœœ˜››ž±ÀÆÎÐÕÔÕØÚÚÚÚÜØÓÌÀ¬˜zjieghoiqtuwuwvy{ywux}yv{z}€yw{vx{{wzyy{z|v|~|~~{||}vv€z~~~‚|~}y|~w{rytwvsx|Ф¨ª«ª›€¢¢¢¡¢£¡¥¡¢ ›£ ›œ¡¡šœš››˜œššœšž› ž§ ¦¦¥¦¬«¯­ª¬¬§¯¨§¢¡””›‚wvjaa^\Xaf`gdhiiihnnlkihmmmlkklnmkmmnnlhjjlmounqvywpzyz}zy||‚{z„ƒ„‡€ƒƒ‡ˆ„†ƒƒ€‚†~~…‚ƒ€„€‡…ˆ†€†‚‡……†‡„‡ƒˆ†„……ˆ…††„…ƒ„…‡‰‡…ƒ…ƒ‡ˆ„‡ˆ‰…Šˆ†…‡‡ˆ„…Œ„ƒ‹†ƒ„„ƒ„„ƒ„ƒ€‡……‡ƒ„„„ƒ…‚ƒƒ…ˆ†„‡„‡†Š„„…†…ŠŠŽ†„„€€„†ƒ†…ƒ€ƒ„~‚‡†ƒ~ƒƒ†~„ƒ€ƒƒ}‚}„~y||ywzywuspqsifghptvz„‡‹Œ’–™˜™ŸŸŸ£ž¢¡››”–™–š——›—›˜œššŸž›š™—™—š˜š™˜™™——žšš˜˜™šžžššœŸŸŸœœ˜››ž±ÀÆÎÐÕÔÕØÚÚÚÚÜØÓÌÀ¬˜zjieghoiqtuwuwvy{ywux}yv{z}€yw{vx{{wzyy{z|v|~|~~{||}vv€z~~~‚|~}y|~w{rytwvsx|Ф¨ª«ª›€¤¤ž›¡ŸŸ   ›Ÿššœšœœ™˜™™–œš›œ˜›™˜—Ÿ¡¥¤¨ªª«­°«ª°««¨§žž–”{wonf`\_Y_`aeebdlhjjiklidjhlkejhjliflhogikohlompwsvxxzwy|zz€}z~||}|‚…ƒ…‚ƒ…€€‡ƒƒ„„}€‚‚…ƒ‚€‚ƒ‚‰‡‡…‚‡†‰‡ŠŠˆ††…ˆ†ˆˆ„††Š†ƒ„…ˆ„„ƒƒ…††…………†„‚††ˆ„„…„ˆƒ†‡‰€„€€„ƒƒ„„~ƒ„Š{‚€‚‚€€ˆ‚…ƒ‡„ƒ†ƒ†‚‚„†‚„ˆ††„‚„}…€ƒ~‚…€€ƒ‚€†„|z|…ƒ‚~ƒƒ…~ƒ~}{xz{||wtstvmoijhenmxz€€‹‰‘–—•™›¡Ÿ¤ŸŸ™˜˜™š˜—š—’™›”šœ—™™–œ›šš˜˜š——˜˜™›—›–›žŸ›˜š›˜šœž›œŸ™›œœš˜–˜©ºÂÈÐÒÕÖרÚÚÙÚÛ×ÑȺ£Œoeahfipooxtwuvxyy}zzuzz|z|{zy|vwt|xuzx{~{}|zx}€|{|{vvzy|€|~~}|}y{wsr{zz~~‰•’‹|gM  £ž ¢ŸœŸ¢œ¢››œ˜™ ššž–›˜››™›—•™ ž››šŸŸ¤¦¨§©««««®¬­©§§¦Ÿœ”“‹ƒz~kkabY^[c\ca``ghmohfcjgjejfcghlllekhjkhffsgkknpxxswuxx{€y|{|}x~|z„…‚†‡}~†€„„{„~……‚‚ƒ}†ƒ€‚‚‚‚†ƒˆ†‡…ˆ…„††„††Šƒ…‡ƒƒ†ƒ†…‚ƒƒ…‡†‡†…ƒ…ƒˆ†„ƒ…€……ˆ†ƒ‰…Š‚}…ƒ~ƒ†€€ƒ‚…„ˆŠ€ƒƒ~‚„ƒƒ„†‚‡„†…„†ƒ„ˆƒƒ‡†„…‚~‚}€|~„ƒ{„€€~€€ƒ…}ƒ€‚€~~€{}{{z|z{~wttpoongghffrvz~}„‡Œ‘––šžžŸ£››š—™™˜™™˜——˜ž•š˜žš›šš™™—™™—™—šœœœ›™š™˜—š˜šž›šœ›œ˜œœšœ›š—–˜¡®ÁÇËÐÕÕרÙÚÜÜÛÙÔÍò›libfgmiqutsw|vsx{yvyzzz|}~xxvy|y|y~vz}|{x}z|zw}z}zz{~{}yz{}}~}~~|wxv~~‚€zp\O@:ŸŸ›žŸœŸ¡¡ž™™™—›šœ™›™˜˜š›š˜˜˜™šœ˜™ž¤¢¦©©©¯­­««¯ª¬¦¤£š–—ˆ…|yrn^^ZSVVX]\dacinjmjhgecijlfhgcjgikggkjjikimopoqsswyz}}~€|€}{zx{„€ƒƒ‚ƒ~„†„†…†ƒ†ƒƒƒ‚ƒ‚†…€ƒƒƒ„„††„‡„‚…ˆ†ƒƒ„‚ƒ„†„„ƒ†„‚‚„†‚‡†ƒƒ„†„‚ƒ†…‰‰Œˆ„‚‰ƒƒƒ‚‚€€ˆ‰‚„ƒ„~}€„…††„…………ƒ…„}‚‡…„…ˆƒ„ƒ…‚‚€~€†‚€}‚‚~€}…€‚„€ƒ‚‚‚ƒ}‚€‚}€~}‚|~zxuvyxtwqqtojjmceqtz{~ˆ‰‘”œ˜š›ž£ Ÿ ›žœ•˜•—›™“™š˜š˜™—›˜˜ œ™›šœš™š™–™˜››˜š›———˜—›—›š˜š œ™œ™›—•–¥´ÂÉÍÐÔ×××ÙÙÚÚÚ×ÑÉ»©Œzfagholrqurvwrtwuwxyt{wvy|{~y~|{|w{z|||}}z€}x|{w~{y}yywz~~€€y‚€€}}{|x€~|r_SFC27/››žžŸ £žŸŸšœ››¢ž™œ˜ž›—š˜—š›š™œ¡ ¨¥ª¨¯¨¬©ª®­§¨ª¥¢ —“”Љ|xnra^ZWX\]_^fchighfinkghellgfkikglgfmsgjkgrnqnpnrwuwxy|wxz~~‚|}}€z~~}……„…ƒ‚ƒ„€„‚‚„‚€„‚†‹€…„‚………†„……‰†‚…ƒ„ƒ‡…ˆ‚……‚ƒ‡€…ƒ„ƒ†…„„…‡…ƒ„‚‚ˆ„„{…ƒ†ƒˆ†ƒ}|‚‚†ˆ…‹„†„‚„ƒ„„†‚…‚‚…ƒ‚„‚†††ƒ„ˆƒ…„€ƒ{ƒ‚ƒ‚}|„|~ƒ€|~|‚~‚ƒ€~€‚€ƒ}~|€x~€z~zwztrrrmoqojfgkjsuyƒ†‡Œ“™•˜šœœž¢Ÿžœ›œ›™™™—˜™›œ™œššš››žŸ™šœœ˜™šœ™›˜šœ›šœœ™šœ›™˜›œ™›™œ˜—š˜•œ©ºÃËÎÔÓÔØØÚØÙÛØÕÌijž‚ogfmljposqtv{wv}wzuy}vwzwwv|||wuxzx|}|~}{v{~{y|~{|x|}{~||}€}€{€~€}€‚}€{l_I@12./1››ž›š›¡š›˜œ›™™œ›™˜š™œš—›˜–›££¢¤¦©§ª¨ª­©¬¬¬©¨ª ›š–ŒŒ…yyqja^[\Z[Y[a]efbcefleigchipigkkihgfckgcdnimmmirrttrut}wv|x}~}y~|~€~‚€}€ƒƒ€€€ˆ‚ƒƒ€ƒ€‚„„€‚€ƒ…‚„„…ƒ†‡ˆ†ˆ†‰Š…„…€…‡Œ„……ƒˆŠ…‰„„Œ…ƒ‚€|……‚ƒ‡„}ƒ€†ƒ‚}ƒ‚‚€ƒ}€€††‚ƒ„~{|€€~ƒƒ‚€ƒ„ƒ„‚‚„€ƒ€€ƒ‚…ˆ‚„…€‚„…€~‚}ƒ}}~||€z|}‚{~€‚w{}‚}}||}twxxtsqpoqgoigflouv|‚‡‹‹•–•›™ž¡¢ ™ž›šœš˜š¡œŸ›œ›Ÿ›™˜™™šœœš™˜š˜š—œ–—™šœšœ—ž™›™™˜ž”™š™š›•–¡³¾ÇÍÒÓÕ×ØÙØØÙÚ×Ô̽ª”znihhlnqpsuuuvyzuywyzw|ww~xzxuxzy}z{|~z{{}{uz}z~~zz|~}|}~~}~}ƒƒ|‚~}wiaR<5.1/.,œœœ œ››˜žžœŸšžš›ž™žœš˜˜š››ž–™›››Ÿ  §¥ª¦§©«­­¦¨ª¥¨¤£ž•ކ‰ƒ|qhaZZYZ[_ch`bdddihgegihhcgfknjdggegifejnloplmmquxsw{|}y}y{{z{{}ƒ}…~€€„€ƒ‚€ƒƒ…ƒ‡ƒ}ƒ~„‚‚‚†‚€ƒ‡‡…Š‚„……‚ƒ„……„‡‡…‚€„……ƒ‡ƒ„Š‚‚ƒ…†~†‚†‚‚€‚ˆƒˆƒƒ~‚€~€}„}„€‚ƒƒ€†€€„„€‚…€„€ƒ‚€…„‚‚ƒƒ~~‚}€~‚€~€€|€|‚€„€€€€€„~}xw{pvvzvpsuqknjnjgdmqx|€ˆ‹‰”–—›šŸ ¢Ÿ£››Ÿœ—šœ˜—›› ¡žžž œœ›ž›››››ššŸ™œž››™™˜žšž–™›˜•›™—˜™š›––•—›¨¸ÃÊÎÒÕÕØ×ÙÙÚÛÙÖÐÆ·£Œnjddjilmosnuu{zuxvuz|yuyyvwxwwuxxwwyxy||}z{v~z|zz{}~|z|||}‚{|||„ˆƒƒ‚~rk[M=023/.33œœœŸŸ›–”Ÿžœ›ž›Ÿž˜››šœ–™›œ›š˜™œ¦¢¤¦¨©ª««««¬®«ª¦§§ “Їƒ~tjk`\Y[VWW^c]bf`dhfefiigilkkghhggjhhngdehhkqmqrortttwwy~{|z}|€|ƒ~ƒ~ƒ}„€~€~€ˆ‚„†„‚‚†‚‚ƒ…‚…ƒ‚ˆ„†„†ƒƒƒ…„††ƒ…†††…~„ƒ‡……ƒ„ˆ…„„€…‚ˆ„…€„‚‚„…‚ƒ‡„„‚‚ƒƒ‚…ƒ„……‚‚ˆ‚…‚~‡€ƒ„‚………†Œ…„€†‚‡|}‡…‡ƒ†~„€‚‚‚€‚ƒ~ƒ~†€}„‚|}€}}zwwzyuqrulmmnhlhlijrw€„…Š‘–”—™™   ¡œœš˜ ››™šœžŸ›žžž˜žœ›Ÿžœœ™›—›šššžœž˜™˜˜™™š•“—›™™™ž™—–—™›–••—›­»ÆËÐÔÔÖרÚÚÝÚØÕÌ¿¯—~khjhhjqprosuyzxx{uzyxz}xuzxzzxwxuwx}xx{|x|{{|}|y|yz|}{|}}~}~€…†‡‡|wiWH85,.+0.-0žžœ™Ÿœ œžœœ™œœœ›¢›žž—œ™™™š—œœ£¥¢£§¬©§ª©ª¥¥¬¤«§¦§¢Ÿ•ŽŒŠƒ}ulfd_TZYWa_X_adegkiggijimklkikefaigefigfcilrqmspsrvwxuxzy{z{z‚‚y}~|‚|Š€ƒ‚„„†‚‡ƒ‚€ƒ€„„~ƒ‡€€„ƒ‡ˆ……„‚„„…‡†„ƒ…‚‚„†…ˆƒƒ†€„‚ƒ‡„„‚†€‚„ˆ…„„…‡ƒƒ‡‚…‚‚~‚‚„„„‚„†€€ƒ}‚„‚…‚ˆ‡‰………‡……‚ƒŠ‚ƒ‚ƒƒ†€‡…ƒ€‚€€~„ƒ€|ƒƒ€|†~{}}}€€{{€~„„}y€}xxyzyttpqqqookojgfinyx…‰“’––—™›£žŸ Ÿ Ÿ™›› ™›žž œžŸ¢£Ÿ¡  žžœ ›Ÿœ˜œššž›š›š™š˜˜—˜™›š›š˜œ— š“———˜¦µÃÉÍÒÔÕÚÙÜÙÛÚÛÙÒȹ§Žtigjgjknqpvssv{xxvxy{yy|zxuz~suyv|wzy{yy{wwy{yz~zyz|€€}|x{|€z€…ƒƒŠ€|n_I<23*.,0/02 œœŸ›šžœ›š Ÿ›ž˜ž –›››˜œ›šŸŸ¤¦¦¦¨«§§¬«©¨©¨©¦££žž”“‰†{smig[W[`\\Y_bdefckiiionjjmkqlgiggkifdggjfioqpnqsrwzyttx{ywwy{z~~|}}€z€}€‚€€€ƒƒ„ƒ†…ƒ…†…‚…‚ƒƒ€€„…†|‡„ƒ†„ƒ……ŠŠ„ˆƒ‰…‚ƒ‰…„‚„ˆ…‡~‚‡„~ˆ†‚~‚†„€‡ƒ…„ƒ‚„†‚‚€…„‚ƒ€€‚†„…‚|€‚†€„‰ˆ††‹……‚‡ƒƒ„ƒ‚ƒ„„„ƒ€‚ƒ~ƒŠ€†ƒ~‚~†‚~€~€ƒ}„ƒ~|~~€„~€ƒ‚‚|x|}z|usypjnmqrppkgdgkuzƒ„Ž”™š™›¢¡¡£ŸŸœ ›œœœ¤Ÿ ¢¡£œ¡¢ ŸŸžšŸš›¢˜›œŸ›››š–œšœ™–›˜—œ•–šš™–™–˜–˜”“––›ª»ÄÉÎÒÕÖ×ÙÚÛÛÝØØÎųœ„kgkintsltspt}rxvv{wt|vzx{x}{wyy||vz}vzx{{{}||}}vz|}‚‚~|‚}}}…‰……|tm_G9/10*(0,210-*15›Ÿ™–šœŸžš›œ›™™™œœ›—š™˜ £Ÿ¡¥¦©¨¦¥§¦§¦¦£¨¤¥¦£ ¡žž˜“‘Їzplh_X\ZTX^_W`hdoedemhehigknlikjcjniegfdfhgflopoqtqyuuuxw}y~}{}yyƒ~}€~€„…‚„ƒ„ƒ€‚„„„„„…‡…~‚‚ƒ„ƒ‚ˆˆ…€……„‡„‡„…‘‰†……†~ƒ†€„†‡Š…„†„‚ƒŽƒ‡†ƒ‚ƒ…‡‡†„‡‡‰‡‡…‚Œ†…{„}‚„‚ˆƒƒ‚~‚„‚‚„ƒŠŠ…‡‡‰ˆ…„‚„ƒ…ƒ€‚ˆ€„ƒƒƒ„……ƒ€ƒ€{‡ƒ~€€~}~}„ƒ…€ƒ‚ƒ‚~ˆ€„€|~}~{tqrqqruxoomtiliifpryŒ—”“–˜™š¢ ¢£¡¡¤£¡Ÿ¡ŸŸŸ££¤¢£ ¢¡¢Ÿ¡œ¢¡ž¡¡œžžœž›˜›››–˜œ™ž•˜›•—•™›—–——–˜–””•“˜ª·ÂÌÏÓ×ÙØØÙÜÚÜÛ×ÑÊ»«Žwdgpkhjnqtuttwyywxv{w{y}wyz}~{yzyz~|~~{z€~~}€‚}„ƒ„‡‰ƒ‚|q`P=/-2+-.(13/0,(-5Ÿ›žššššŸ¡ŸžŸ ›™šœš›šž  ¡¢¦¦§§¨¦¥©§ª¦¥§§¢¡©¤£¡ ›™•ކƒ}tmd^d^W^X]a_bgibdegejdhnigjggmhgigibbheehmnkmrprxryvvvvxz}yz|€|x|€ƒ‚€ƒ~~ƒƒ…‡‚„}ˆ‚„„„„„…‚……ƒ„‚„‡‚‚€‡‚‡„ˆ„ƒ†‚Љ†ƒ‚„ƒˆ‚†‡ƒ…‚ˆ€ƒ‚‰‹ƒ…ƒ†‚Šƒ‚„ƒ€‡…‚…Žƒ‚‚~€€‡ƒˆ„~€}…‚†„‰ˆˆˆˆ‡ˆ††„ƒ…‡‚ƒ„‚‚‚……‡‚…ƒ‚ƒ~{€‚‡„€ƒ‚{~€~}}ƒ€ƒ‚‚~„„{|€‚€}}}|{yttupqvtmplqhejfgnsx€‰‹Ž–š›šž ŸŸ¡¦¡ ¡ ¤¢ Ÿ¡Ÿ¦£¥¢¥¡ž¤¢£¥ŸžŸœœž  ¡¡š›šš›—žš™š˜™™š–—˜˜—š˜—––””“’’™¬»ÇÌÑÔ×ÚÙÚÙÜÛÚÛÔÍÆ±œ‡ifghmlkopuozrxytwxy}}x}|‚~zw{{z{†y||y|w}|‚~~€ƒ€‡‚„‡†ŠŠ{n]S92+(.4..+2.2.01.2ššš› œŸ›¡™ŸŸ Ÿ œž›œŸœ™šš›Ÿ£¡¥¦¥¥¢¦©£¨¥¦ª£¤ ¦¦£¡ššœ•”ˆ~q_cXVWY___b`bkcdcilnkirlkjhhihmgjmfnifejkorunusss{{}zxyxv}~zz{‚}€‚ƒƒ‚€ƒƒ„ƒ‚…‚…‰…ƒ……‡ƒ‚€ƒ……‚€€…‚„€†ƒ€‚†„‡†…„„€„…†‡€„„‚ƒƒ€‚‚„‹‡††‚„„‚…†ƒƒƒ‚€„ˆ‰ƒ‚„†ˆ„„‚‚‡€‡„Œ€~ƒ…‚‚ƒ‡†ƒ…ŠŒ‡…†‰ƒƒ…‚††„ƒ‡‚‘€~€ƒƒƒ‚‚ˆƒ~„}ƒ…}„†„„„}~}‚ƒ€~yƒ{z~zvxvxsqtrlomqkleehmqsz‚ˆ‹’˜•š™žžž¢¡££¢¢¡¥¡¢œ¢¢¡¡ ¦¦£¢£¢£¡Ÿ›žŸŸ  Ÿœ›šššœ››™™›–———™——œ–•—˜›™—•“—’—Ÿ´ÀÉÏÓÖ×ÚÚÙÚÛÛÛ×ÓË¿­’uggfhjjmqrruprwuxy~x}wyy{|x{z|{zx~{{y||‚}}‚‚~}ƒ‡†‡ˆ†ˆ‡ƒ~mbLA0&-22-2/1<24,*,-4œœ›¡›œœ™žžŸ›žœŸ¢¢›Ÿ™™Ÿžš—› žž ¢£¤¥§¥¤§«¨¤¤¢£¥¤ž¡££¡›œ‘“‰‰ƒtg^bUWZY_`_dbfjigjkmihnljfggeigeejldhggdilqpprroszuuwwxvz~z|t{|€}|~€€‚€ƒ‚„ƒ€~€†„„‡„ƒ………‚ƒƒ‚‚‚…ƒ‡…‚ƒƒ†„‚„…†~‚ƒƒ…‚‚‚…ƒ„„‚ˆ€ƒ„…†~‚„„…‚‰…€‚†€„…ƒ‹ˆ…€ƒ‚„…ƒƒ„}~€~~†€……†‡‡……ŒŽ‰†„‚…‚„†ƒ}Š~†Ž†|……|€„€‚‚ˆ„|{}~‚ƒƒ‚}~}‚}‚~y}z€{xŠyzxtosslnjimidfmquz‚‡Š””“˜™žœœ£¤¢¢£¡¤¡£Ÿ¡Ÿ¥¥¤£¢¥¤ £¡ ¡ŸœœŸŸ œ¤Ÿ˜šžžœš™˜™——š—œ™›š™–•™˜•—–œ•‘˜ªºÅÌÏÒרÙÙÛÜÝÞÛÕÏÆ¹¢ˆqcppmkqvrqmuxuwszyyyx{{zzx}uv~ztxzyy‚~€~~ƒ‚„„‡…†ŒŒ‡„}pdR?43++7C52-/94/2.5.4›››šœœœŸ—›¡ŸŸ›œžŸœ™™™š˜¡¢§§¦¨¦¨ £§¡¥¥¦¤¤¡¡Ÿ¤¡£ œ¡”’ŽŠƒ~yoid]cWV^]bb\fcgigfwkjjegimgkni_dgbgmcgeghkoppqsptqxrvzzx~z{‚}~|€{yy€ƒ~ƒƒ€„…‚‚~…ƒ„€…€„‚‚…‚‚…„„‚‚„ƒ‡†ƒ„„ƒƒ†€‚~…†€ƒˆ„‚€„‡…††‚€ƒ€ƒˆ‘ƒ„ƒ‚ƒƒƒ‰ƒ†ƒƒ‚‚ƒ~€€ƒ„„ˆ‡‰ŒŠƒ‰‡‚ƒ~ƒ‡ƒˆ„„~€€€ƒ|„ƒ€{|}~~}|{~ƒ|{€€ƒ~~‚€‚~}~z{{{{{…|yqoprpojnjpkkgmoos€„‹‘›“™š™›Ÿ¢£¢¤¢¤¦§ ¢ž¡Ÿ ¢ ¡ ¢¡¡ž¢£ž¢ ˜ž›žœš›››œ›˜ ™—–™˜—™˜———˜™˜˜”˜™”–—”‘‘“ ²½ÉÏÑÔÕ×ÙÚÛÛÜÛØÖÌį“xhimqjiqrqnuwtxtwzsx{~t|s{{||x||~|€††€~ƒ€ƒ‚‚†‚†‡Š…ŠŽ†n_G<4.--))1151,)+0/1/72ŸŸ›™—œ˜žžžŸŸ¢žžžž›•›š›¢¢§¤§ª¦¥¢¥¥£¨¢¥¢¢  œŸ ž˜•—‰Š„yyib[]WZa_`_ebccgahjggihdcfgmhadhdkfhhfhinnmusoouysw|uuxxwu€}}zz{~~}€‚ƒ…ƒ€ƒ€„ƒ„€‚€~€ƒ‚‚€…€ƒ……ƒƒ…‚‚„ƒƒƒ†…€„…ƒ€†„…ƒ€‚~„ƒ†‚ƒ‚…ˆƒ‚„ƒ‡‚{‚„„ƒ€~‚‚„‚€~€„‚ƒ€„~‚~~€„‚ƒ……†ˆ…ˆƒ‚‚‚ƒƒƒ„ƒ„‚„ƒ‚…‚‚€~„~‚‚|}|}{~~|ƒ€€}€~€‚€}|}ƒ€~}~y{y|{xxyztrupolqukjjfginlq~ƒˆ‹‘–˜šš››žž¤ž¤££¡¥ Ÿ££Ÿ ¡  ¢¥Ÿ££ ¢¡£ š—žžž›œ›˜œ›˜™š–—š˜˜˜——›––˜˜˜”•–—”––Ž–§¸ÄÉÑÑÖרÙÚÚÚÛÜØÔÌ»¤‰ljfijlousmyqxwtvvrsry|}~{xwzwuzyu|€}†„…ƒ‚†‚‡‡Š‡Ž…s[G615)*-,-65>/34+011846œœ™˜œœœ¢œ žž ›œžšŸšœ›› ¢¦©§ª¦¦ªŸ¤¥£¤£££ £¡ ¥¤››—˜‹……~tec`VYWX^_acfbckkhjgefghkgeghgknmgcekihjkgjjlloqqsqt}xu|yvy~x}~|‚†€€†‚€ƒ„„†‚‡…ƒ€„€~…€€ƒƒƒ„ˆ‚€‚ƒ…„…„‚…ƒ€€„€…}‰€|ƒƒ‡€~ƒƒ„‚~€€„€ƒ|}€„~…|‚ƒ‚„ˆ}€ƒ…€€„…‡‹ƒ„ƒƒ„„†ƒ…ƒ‚~~ƒ‚€‚„‚„…ƒƒ~‚€„ƒ{~~}|}€|~ƒ~|€{|~‡€~~{|x}wxxv{rsqqllppikmimppry„‹’“•“˜žœ¤¢¢¡£¥¡Ÿ¡£  ¢Ÿ ž ¤¢Ÿ Ÿ ¢¡Ÿ¡››œ™œœžž™™š˜šš——›˜–•™˜™—˜–––•”˜—•™˜““Ÿ¬¿ÇÌÐÔØØÙÛÙÜÛÝÛÕγ™~qlelslmoqtrtxruysxvzwy{xyy{t{y{xy||~€„ƒ…‡……ŠˆŒ}qaG83::.6?32063///000//0=œœ˜˜Ÿ¡Ÿ ›ŸžžžŸŸ¢›œžœ žšŸ¢£¤§¦¥¥©©¦£¦¤£¥ ¥¦›žŸž ¡Ÿœš›–Š€xqjc]YYYV\^a]^cfefknfjgihgceojkgdgijcehhkkokjmtlopsxxw}y{|xy{u|ƒ}‚€ƒ€}xƒ|‹~‚ƒ‚~~‚‚ƒ‚ƒ~‚‚ƒƒ€ƒ}…‚€„€…„ˆ†…‚†„‚„‚‚ƒ‚ˆ„†ƒ‚‚…†}„„„€€€~ƒ„~ƒ||~~{‚€€}€€‚€~‚~}……‚€…ƒ†‚‚‰‡‚……€……€„‡‚‡€ƒƒ~~€„‚€€{~€~z}}€}‚}|€|{€}|€€€||yxy{yxvtunonolknjjklovuu}……‡Ž•–—˜›žŸž¤¡¥¥£¤ ¡£§£¡›¡ ¡Ÿ¢¡ ž¢ž¡ ¢ œœ™œŸ›Ÿ›™ž›œ™–™”œ›˜–˜–˜—˜š˜–—–—••–‘’–§´ÅÉÏÔÕÙÚÚÜÛÛÜÜØÓËÀ©–tfjhnlpqpvv{|vz}ytwzvzxyyxzwvxw{yw~}~‚„ЇŒ…„€q^J;K?/0+-0,2(,4,-,6147766››™š—žšœ£¢¡žœŸŸžœœžžŸ¢ ¤§¦¨¨¥§¨¤§¥¡££££¢¢¢Ÿ£——’Œ†}xrg`_XURX[e_]aa`eikingmjjhdhjmfhghechjbhhnkpqonqpsqxw{yy{xvz}x~~}€}‚€|{…ƒ‚‚„„ƒ‚‚„„{†€‚€~€‚ƒƒ‚~€„…ˆ………~ƒ€€€…~€‚‚“„†ƒ†ˆ‚€€~‚}}}y}{~~~|}~}€z€|~~‚‚~zƒ|~€~ƒ€†„„€†‚‚„‚ƒ…‚ƒ‚†}€€~‚~{|€€€€‚z}{€}€€‚}~|}|}~€…}~{~}{y€…zutpoklnmjiihnppxy€~ˆ‘”›ššžŸ¡£¡¢¢£¡¢¢¨¦£¡ž ¡  Ÿ¡ŸžŸ¢ŸŸŸ ž˜œ—›ž›˜››™šœœ™šš˜˜™›˜——˜—˜˜–˜–”—“••““‘“­½ÆËÑÕÕÙÙÜÛÝÝÜÛÖÐǺ¥ƒjedjjnkrwrvyzwzzv~|{}|y|}zzxyztv}|~zz}„„‚€…‡ŠŒ†‚o^H8>JJ10(/+.0('..,*/13476:™™™œ™žœžœŸ žŸœžœœ£žœ›œ› ¡§ £¥¦¦¨¦¨©¦¦¤¡¢ Ÿ£ŸŸŸ ¡¡ ¢œ–š•Œ…ztmc\VWVNSTa]_aedhgeidlekghgijehfdeilgfeihljnolkqrqrwzzwxw~vzz|}{{}~~ƒ‚|ƒ|}ƒƒ‚„}ƒ}‚€}ƒƒƒ‚„ƒƒ„€€‚€~}‚€~‰€€„…‚ƒ~‚‚ˆ…ˆ††€‹Œ‚~…ƒƒƒy~z~|€~{}~€{„~€ƒ€|}}}~ƒ|y~€y„~‚…„ƒ„†…‚‚„ƒ‡‚ƒ†‚~~|~}~~€|ˆ}|€ƒ€}‚€{€‚€|}{yy|||wvrmqqlkjhjjmlsqvw‚‰‹Ž“™œ¢ž¢¡ ¤ ¡¡£¢¢ ŸŸž œ¤Ÿž žž žŸžžž›¡˜œšœœ›˜™˜š˜—–š˜˜š—˜˜˜—™”˜—–™“•’‘“’£³ÄÌÎÔÕÙØÙÛÝÜÞÝÛÕβ’ydhjlmnprrts|}z{~~y|{xz|vxwxzx|}}w{{€‡†ˆ‡‡‰~o`R52'0;4.&'%)/**.-.//73;:8=6854285/,1//44¤¤ ¢¢ŸžŸœ ¡Ÿ¤¡ž¤ §§¨©©©¦¢¤¥¤¥¡ž¤¢¤¦ Ÿ¢ŸŸžžžž£¡¡¢ž£¢ž›œ™‘‘‰ˆ~vm`W[WRSSV\ccccfdfeehmoefjfefegcjigdjjfbehhhpqmqpvutuvux{vuzus{wv||}{}||}|{}€€z|zyx}‚|…~„‚ƒˆ‚…~‚‚~{z€…€‡}€€ƒƒƒ„†~„€~€„ƒƒ€|~ƒ€~€}{~~{„zzzxy|‚yz|yvzzz|zutu|uxxztw{w|z~zz~}|€‚~|{…ƒƒ†‚ƒƒ€~~}€€z€}‚‚„~}~‚€‚€€|}~{|{{~~€|}~}z{|zxwsvstsussupoqjmnmsvw|~ƒ„†‰ŽŽ””’˜“–˜˜›šš™›œœžœ¢  œ›žœœœ›œŸ¡™›ž™›œœ››™•™œŸœŸœŸ›ž˜Ÿœš ˜š•™—œ””’•–‘“•‘”•ŽŽ‹™°ÀÉÏÓ×ÚÜÝÞÞßáàßÝ×ÐŰubbcqoplmprrvxyqwy}}ƒ€€„„†xl]K5,'-(.1,11-(/;ACKD???@9>3451343.1--..:¤¤¦¢£¡£ ¢£¢¡¢¡§¥¤¥§ª¨¦¥¥¥¥£ ¡¤¥¤§¦ž¡¤œ¡žŸŸ ¢¡¡ ¢¡¢¥žš™’އzthd`YSOQU\d\[ebjjggidgcdigkfffefddmechfkfjijmtpssvrrxxuyu{zyz}xxyzy~wyy{€~€{{}~}€~~|€y}z|{xƒ€|…€ƒ‚‚|ƒ~~„‚|‚~~€„„„‚€‚€ƒ‚…}~‚~}‚~}€„v‚yyy€|{xvsywzyzy{yy{uwxw{xxv{zw|}}~ƒ‚‚„…ƒ‚~€{}‚|z~}ƒ€}€}~€yz€~€€€}}z~|{~€‚}~||~{zxuyrstuwvsnurqstmmorrz||‚„Ћޔ–•“”™˜ž˜š˜œ ››š œ™ žžš ˜›š˜˜›——š™˜›šžž››™›žš™˜•›˜™˜”–•–—”““‘ŽŒŽ‹“¦·ÅÍÔÖØÚÝÝÞÝààáÞÚÒÉÀ¢„kdehhrhkponruswxz{y€…„…yp[K91-&-2,)*.4-)-02375181435101//040--/,/2££¡ž¡¤Ÿ¡ž ¤¤¡ ¦£¥¨ª«ª¨§¦¤¥£  £¢¤  ¡ ¢¡ ¢ŸŸ¡¡žŸ£¡¢Ÿ¡š—“‹‰ysfd_XTQSXZYcWYlffqifleccdefehfcdkefcibgffchghknporrv{xyuxxzwx~zx€vwz|xz{w{~€|{}{€z}y||~}~{}}‚€|‚‚€|„€‚‚‚}€ƒ„‚†|€€}€€ƒƒ|€€†|€z‚€}~||z€}}~}€zzzzxutvxyuuyvxyxxyvxs{xyyx|€€|ƒƒ†„„~‚ƒ€~‚|~ƒ~‚}€~{€‚~}|}€}‚}}{ƒ€~z‡~{‚~}x|zyxxsuqtrusnxunommsnps~z|‚…†‡‹’Ž‘““”“–˜˜™ž™œœžšœœŸ›ŸŸš™œœžšœ˜ž™œžš™˜™˜š˜œ˜šŸž œš™šœ˜™™—˜˜–••–””–’”ŠŒ‹™®ÀÌÑÕ×ÚÚÝÞÞààáÞÜÕÎÆ®•xdbimkmjpquqqoqtx|z}„ƒ~|qaQ56/+,,-*.)+/4/4:55393<42.57711-.1..-,/),  ž¡¢Ÿž ›žŸ¢ Ÿ¥¥©ª¨«¨¥¢¡ ¢¥  ¡ŸžŸœž¤¥¡¡ž› ¢¡¢¡Ÿ¡¢¢ š–Ž‹ˆ~|pie]VYQQSZ]Xbdjgfchfdkfhhfigijmackddaiehggjjjnqnpssrzxyyzuwxwz{|y|€y}{y€{z}|ƒ~{}||€|€€ˆ}„}{}~ƒ}{ƒ‚|€ƒ„ƒƒ€‚‚ƒ€ƒ~‡‚‚‚ƒ‚}~}ƒ}|}‡„}}{€{}{|y}zxxwtwvz{wzxttvwyurtvxwx}|~€}ƒ€†‚€|z~€}€x|‚„‚‰ƒ~}}}ƒƒ…|~~€}}€}||‚}|}|}z{zzxvqyusrswsqxsnqmlnqtxx~|ˆ‰…Ž‘““—•––™™š˜š—œšžžšžŸ™šœœœ›™›š››™–šš˜š™™š™ššœšžššœ›››˜™—–•’”—–‘••‘—‘‘’ŒŒŠŒŒŒ”£µÄËÒ×ÚÛÜÝÞßÞßßàÚÔ˺¢†lchgmhimkqoqltryz|„ƒvq^Q=/.+-))*+.+,40.6669<=64461:1341052,/---)1ŸŸ¤ ¢£¥¡£ Ÿ¢¡£¦§©©ª§¬¥¡¡¡ŸŸžœž žžž™Ÿ ¢Ÿ¦Ÿœ Ÿ  ¡ ¡¡¢š•’Ž–ymb^WUOZWWa_\_ecdgidfjgkmjigenkcfljfhcclifilmlomqrqs||w{yxuuuwy}{x~{€}z{y~x}}ƒ€v{z|z}y‚„‚„€‚}€€€€}~{}€‚€}€€~€~~„„}}}„€€|ƒ}€‚~}†‚€}€{~{~{vz~||zy|rzuvqxxqw{uvtzwytxu}zzz|}~z}€€€‚~ƒ|{€€€€€€|€€„‚‚…}~~€ƒƒ‚||€{}{{€|‚}‚~uwwx~{xuv|urtrtqqoonmnjmmrrxz‡Š‹Œ‹”’”“•••—š›š™˜œŸž ›™™››š™›š›Ÿ—œšœœš™™šœ™˜šœ—– œ›——›˜——•—“’’’’‘Ž‘’ŽŒ‹‘‹–¯½ÈÏÔØÛÝÜÞßÝßàÞßØÎÆ³”xdagjlmljoltqvwzz}‚‰wr_I@,1,(*+*-./000975<9E>>:914642833/.,--03.)-¢¢¡œ¡¤¤ ¡ ¡¢¤§¨­ªª¯¦£Ÿ¡ ›œœœ›™œ›œ ¡ ¤Ÿ ¡žŸž¢¤ ž¢™™”…€|uja`]TOWV[^\adehacmjcegkdejefjpidfgedifeefjmjkmorrwutyuxzzxyz}|z}€€{~{{{}}}}~|{||~~~~€}„€~~}|{~€€‚€„€‚…ƒƒ…€€~~€††~„~{{…‚‚ƒ‚~|}x{}yux|~y€’€€ŒŠ}y}|uuytustxwwxw}~yvxx{{‡}{~}|zƒ€€€€€{‚‚‚ƒ}€‚‚}~|€…„~€}}|~€}|{{y{{||tvzqppstuuqsnklloqprs{€~…‡‹‹Š‘’”•–—–˜—˜—œš›œšžœœ™š˜š——ž—™›˜™™˜œšš›™—™œ˜œ˜˜˜˜”š”—•”“Ž‘‘ŽŒ’¶ÅÌÑÔÙÙÛÞßÞÞààßÛÓÎÁ¨‡kbjflkjnprsptxyy‚‚{kaO92,+)*/0.)11133616<:5;95>7:542452-+,../1/+0¡¡Ÿž¡Ÿ¢   ¢¢¢§¬ª­ª£¢¡›™šœ›šœž›ž› š £¥¤  ŸŸŸ¡ž¡£¢¡¦¡ ˜˜–ˆ‚~qja`XSNRT`\]a`bceddigkfjgcjbfchkifelefgkhhlrmknouxvuwxxysvzvz|{z|yw{€|}||~€~‚~y{{}z€{€‚‰‹~‚~}|}|…†„„€~‚‚‚…|€„€~|‚|}}‚‚„„‚{~~y{zz€z|~}|~{}‚{„”‹«±š¨´¢’𣓋”ƒvz|wtwyv{|{|ww{yz{yx|{~yzz‚}€z€€‚„‚~}~}€€„{‚}}‚‚}}ƒy}€}‚|zzuxwqtvouvrpoogknkotptz‚~†‹Š‹“–“˜‘•–˜šš—–™™™žœž›œššš›˜››š›ššœ˜Ÿ™›™›ž™œœ˜—œžœ™™™˜˜—•”•––“ŒŽ“•Ž‘ŒŠ’Œ˜ªºÈÐÐÖÙÜÝÝßßàßßÝ×ÐȲšvedjfillpypory}xf^Q;/,100+-+,*/2,11562:9:><:5433076<1/+-,/30.1-¤¤Ÿ¡¤Ÿ¢ Ÿž¢££¤¨¦§¨¥ ¤œšŸš™›™–˜–šš™›ž¥¡¤¦£¤Ÿ¡¡¤¢¡¡¤Ÿœšš–…|tig\YWNUS^[YZ__ecjdhfmihmhmijeljkeffehffkhmtmpmvutssx}yvzy{yzxz|{zz}z{y}‚~~{|~y{~€‚ƒ}~€„€„ƒ~~z€ƒ“‚„‚€†ƒŒ‚yƒ~~…€|€‚‰Œ‰y{~}{{€~„•ˆŒ‹‡‰„ˆœŽ’²¸žœ±ºª¢²±¦¦®¸¬ ¤§›†||rwwvvtwywv{zxy€}|}~}|}}z€~~€…‚…€„€‚~€~y|}|€}}{‚}~€{|~|}{xvtwsqupovxqonqnkjoqvswy€…ˆ‰ŒŒ’•–•“———–š›š›˜›—œ™™˜š˜–™™—›˜˜œ™š››™›˜™›™•š˜˜••–“’–“”•“”ŽŽ—‰‘‘ŽŽŒŒ²ÀÌÎÔÖÜÜÝÞßàààßÛÕÏÀ¬lbgvijmuuvty}}|siXR@/-+,,410,*+1..0446<:7:8<4788>5*;2/6,.-/.4-,/££Ÿ¥¤£ Ÿ ¢¤¤£««©«¢  š›–˜•™•–””—˜šœŸ¤¢¢¥¡¡£Ÿ¤¨¤Ÿ¢£Ÿž™“’‚„|kfa[ZQVT]Zcbadagghglhelnqkfdhfdfelfgidgjflrjooprqsvwyyxxz}v{{{zz|z~}~|}|}„~|~z€~„‚}}~€‚ƒ…~‚}}€z}‚„„„…ˆ†~‚‚€~{}||}…„‡~~~~yƒ…}‚šœˆ‘“Œ•š™ž´³œ¢·¯¤§µ±Ÿ¯¾½®±»À¾¯¨§¨zxuqtvqwvquzy|{}z{||}}{}€€|€ƒƒƒ„ƒƒ‚‚€y}~}}}||{…y~}}||{||xxxywwtvuquvrponljpglqquwx„ƒŠ‹‹“Ž”‘‘‘’•”•˜•–™™™š›œ››œ—™–›˜››—›™˜™œœš˜š›œ™™––˜•š—–“˜“”““‘’‘“•’‘ŽŽ‹‘‘ŒŽŒŽ–¨¹ÆÌÓÖØÛÜÞàÞßßàÝÚÒ̽ ~om}poopuos|v}xjYB0-/,.+,111-*.2142296789@>;;88333-0++1+-0.+/00-££  ¢ Ÿ¤ ¢££ª§ª«¬¨¤£Ÿ›š™–—”–•——“””šž¢¦¢ £ £¡ œ¡¥£¤¢¡ž›—™Š‹„‚~mh\_YSRU\Z^_\^_gdfglkhkijneehfldhffjfghiklpoqropqlpwvyz}zwz}|||€|z{}|}||€~‚z}~€}}€‚ƒ‚~…ƒ|}~€€‚~€}„}€ƒ‚‡ƒ~~‚‚‚€€}€€~ˆ„~„{‰Œ„–šŽ˜ Ÿ§­©š¬¹¯¡³³¨£³À·¨²ÂÁ´¯»Äö¤¢ ¡}{yruvuttvy{y{z||z€y~}~ƒ„ƒƒƒ„ƒ‚ƒ„€~€ƒz~€€}ƒ}}}ƒ~}y{zwyyywuqppotorrplmknmprwy||€Šˆ‘’‘–’“––”˜˜˜œšš›ž›š›ž˜–™š—™š™›™œ›››—›šž˜™•˜–––™–––“•’Ž“—’’Ž‘’‘’ޑГŒŒŒŒ‘—°ÀËÎÑ×ÚÜÝßÞÝàßßÜ×ÐÆ¯•tilifnurr||zsnbN<,'/(&**,-*)01233:=43<77=9;984341.**,..10).-005£££ £ ¢ž›ž ¥¨©ª©­ª£ œ›š™’““”’“–’—œš¡£¢ŸŸ¢¢žŸŸ¡¢¨¢££¡œ›™–‹†€yod_\[TRXW[\a`hbcdedfeghfhjdkfgkgjgfbfeehkjppsqrpppqxwxwzyuv{x|{€}|}‚}z~{~{ƒ~~~~|~€…ƒ…‚‚z}}„‚‚‚~{}„ƒ„„€€€€…~€„y|}{~ƒ}„•‘Ž—Œš—“œ–‘‘“–™Ÿœ£³¡”§«³¬­º´¢®¸·°¨ºÆ¹­²½Ãº²¯¹Àû®Ÿ‘zwxswystwsx‚zz|y}}|{€„ƒ„ƒ~‚„ƒ‚‚{~zƒ‚|€~}~~}|~~||uxxyywvunsqrorpmlkjnlppzw}…ƒ†Š‹Ž’“’Œ‘—‘’™—•—™—š›™š™ššš™“•š™˜™™˜˜—˜™››šš›˜––—𙕖—••–“”‘Ž”Œ‰Œ’ŒŽ‹’‘•£¸ÄËÒÖÙÙÚÝÞßààáÞÛÖ˼©†loglpxuw}}vj]L3+&*+-1+-*1--,126278546H;@998??84,.'+,**050261/-¢¢£¡Ÿ¡  Ÿ£¦¤ª«®¦©¨¡ž™—“’“““–™¡£¤§£§Ÿ¡£ ¡¡£¢¤¡¡›žš”‡ƒ€zlcY^QTYY\[^Zdccdbkfjifikidfffdhhkffdgghhnlpnrortntuyz|y|{{zzy}~~z|„|x|z}~z~ƒz|~}€€„‚…€€ƒ‚€€‰ƒ€ƒ‚€}‚{}}…€†‚ƒ€‡}€|}~{y|~Šƒ‘—‘™š“•›’Ÿ¡™““—Ž“ŸŸ¤µ±›Ÿ§²¢¦°´´°¶º¬©°¹º³¯´¹¹²¨²¹º¿ÁÁÁ½»«œ’€vuqr{ptzzvy|{{|z{†‚‚ƒ‚„‚€ˆ}€€€~{||~}|~{~}€}~zzvuyvyx|wmqnspoppkjnmoqsw~}„‚‡ŽˆŒ‘Ž“’Ž‘‘–––“—•š˜œš™™›™›˜™›˜šš™œ™›™”Ÿ—˜™œšœ˜•–˜–›”““‘’–•‘”‘ŒŽ‹ŒŽŒ‹’“—©½ÇÎÒ×ÙÛÛÝàßàâáßÚÒɶnklux}{€tg]H:0*%))**--),-.*/2592:678685;66379-1/)*,++-+/,1+/3¡¡Ÿ¡ŸŸ›¡¢ ¤¦ª¬­ª¬¦¡ ™™“ŽŠŽ’•›Ÿ¤¥¦¤¡ ¥¢¤ ¡£¢£¥¤¢Ÿœ™’Žˆƒtjd]WVRSTYZ\^aX_ccdgmggfiehgbffhikhonggfmlpnorrpquvwuzzyux{wuxx|}{}‚~€||‚}}||~~€{}‡‹€}‚‚‚€{‚„~ƒ‡‚‚~||}ƒ„€€…Š|€~}‚~|€~}~„Š”––—••’•š‰”Ÿ‘˜•œ–¡ª°®Ÿ°¦¡§°©ª¹Áº«¸¶®«ªµº°¨¥²³³·»¿¿»¸½¼½¾»²Žƒrrnoryxwz{yxyz~|}€€…‚€‚}€{~{|~€€z€}€|{|wwuyuyywsruvssnqponnkjhnioptt{‡†ŠŽŠŽ“’‘‘‘’”“’‘‘•”——•—œ›˜š˜›š–™œšž—™œ™›™—™˜˜—˜—œš•™™–˜“““–’•Œ‹ŽŽŠŒ‹‰ŠŒŠŒˆŽ‹•´ÀÊÑÔØÚÚÝààáãâáÞ×ÏÀ«Žxqpxrwz{f]K4-++)*,)*/---+.0-/84173247433668330/',+-+/1-,.1(*/   ›¢£¡¡¨§«¨ªª¥£žšš“’ŽŒ‹’ŒˆŽ“—™ž ¤£¥¥¡¡Ÿ¢¡£¥§¡¢¤¢š—”‰ƒ~xmeaUVSSQYY`][Ygfeefihgiffifcljfeeghlfhcmhmljnuqtttwuxyw}}~uw{xzz}~‚‚~|z~zz~{{ƒ}}‚€„‚€„ƒ‚ˆƒ…ƒ€|‡|†~~~ƒ|€~~}}~„Œ’“˜œ‹ –‘‡”•š‘”——®­¢ªµ­¡§¬©¤·¸¸­¹¹¹¯©¹»¶ Ÿ®®¨¯µÀ¶¯®º¼½¼Áľ»¶¯ž}vuswwsusw|vz}}|~€|~|}‚{}€}€~„{|||}~|€|{uxv{xxux{uwrosqopmmimhknnpwvƒˆˆŒŽ‹Ž›’ŽŽ””˜‘‘—š˜˜™› —œ™—š™–—˜˜–™œš——˜—˜š––™š”™—–‘’“”‘’—“Œ‹ŒŒŽŒŽ’‹’ŽŠ‘‰‘˜¨ºÅÍÑÖÙÚÜÝßàããäãÝÕuptvwxsdL:1**((*-.,),//0*.,33;75249564205201/2,-00/3--0+)1,-¢¢¡ž¡¡¡£¤¨§¨ª¨§¥¥¢›š˜“’މ‡ˆ‰ŠŠ…Ž—šŸ¤¢£¤¤¦£ ££¢¥¦¦££ŸŸœ–“„~vhf_UTQOZWW^]]_fbfcgmhkffjkbllffiifejijlpokkmltrtwrvx{xz~y{ywyy}}{|‚{}~~|{|~}‚}~‡€‚|‚{ƒƒ€ƒƒ‚~z€ƒ~€€~{€€~‚~{}ŠŠ‘––œ“‘‹„’‘Œ–Ž£¦¡ž¯°¢ ¨­¦¯´±¬¸³¶°©´¸¶¬¡©¬«ª­·µ®¨¶»¾¹®¸ÀÅ¿À¾·°£‘‚knssuvx|tƒ{{|}€€|}}~€|~€{zy|w{{|y|}‚v{wwxywxvoqrurprrqqsmjjhknsou|…ƒ‡†ŒŒ‘’“”‘”Ž‘“–””““•–—••˜œ—™™›—”œ••————™–—––—˜”—–““–“’‘”’’‹Ž“ŽŒŒ‹‹Œ‹ŠŠŽ‹‹‹Žˆ‰™«¾ÉÏÔØÙÜÞààááãâáÛÔɶ™~vzywshP:/--)%&),/+-+//1,.25:3<628-154324254-0,3421154*-*,,*   ¡Ÿž¢¤¥©§©«©§ª¡¡œ“„„‚‡‡Š’”›Ÿž¤¢¤¤£¥ ¤¤£¤£¦¢¦ œ¡–’Љzvma^VRTXXV[b`\bh`gcffkhhb_legjhdhdiljfcihlllsquzvustyzzxwz{{|yx}}~z€ƒ~|~~z}~€‚ƒ‹~{‚€ƒ„z€€}{€~zz‚€}||€€€~|z|€~ƒƒŠ˜”ˆŒŽ’’¢•‡“†Ž†””’—š—ª¬£¡¡Ÿ¡¬­¨©³º¬°³¹»«®®²«¨¨¶¯¥¬¹¼»µ®¶»Àľµ¸ÁÇÀÄÁÀ´Ÿ„vrpussvu|vx}{|{}}€~}|ƒ‚|}~zw€}€~{||{w|{zzwyxvtwmptuoomorssnijfjpmsz{}„†…‹’’“’‘’”•“••••–š•™™š˜ž™›™š–•”—™—›••–•““‘”—™”’”•“‘“‘Ž•“”‘Œ‹ŒŠ‰ŠŠ‹ŽŽŽŠŒ‹ŒŠˆ‹›´ÅÌÑÖÙÜÞßàßàãääÞÚÑȱ‘xy~nbP@1''-1*.)-.510/56088:768818.7813-3)).4635/51/00/22+03žž¢£¡£§¦¨¦ª¦«¦§¢¡žž˜“ˆŠƒ‚ƒ†‡‰‘—œ¢¡¥¥¥¤¡¢£ £¢¢§§¢¥££¡˜”އwpbZUJOSWXYYX\^`_^ceffhf`bggecghfghphhgjfgponpwyvutyw{vyuyv|{~z{}~€{~~|}z}~~…ƒ„…zƒ~~~}{ƒ~~}{ƒxz}}~„‚†‚€}}„……€…ƒš’“…‹•‘•›œ“—ŒˆŽ¢ ’–  œ˜ª¢‘¦¯©›¨³­£¨»¿µ²®²±¨²·±¥¨±¼À±¥¯·¿¸³¸º½ÂÃÅ¿¿ÁÆÅ¼¯~poqu|stvt{|~}~{{‚~€ƒ|~|z‚~‚yv}~z{z}zvv|vvy|prrustpqrkqkkljk|ltw|~…ˆŒŠŒ”‘‘–‘‘ޓޔ“””–•˜œ˜—œ™š™˜›™“œ•—“™–𙕖“‘””’“““’”””’’’‘“ŽŒ‡ŒŒŒŽŠŽŽŽ‹‹ŽŠŠ‰“«¾ÊÏÓ×ÚÞÞààáâããâÞ×п¤…zqcZ=3/6%+)((67,1053421757868:244546-/++-0359;011,00*++,.3¢¢£œŸ¡£¦¨­«ªª¦¥¡žœ™—‰‡ˆ}†Œ“˜›ž¡£¦¥¥£¥¡¢¤§¨¦¥¥¥¢¢£™’‘Іxl_\UPPNRWUXX]ac__eheefkcdedgkihigejghijifonknr{sssxxxw{wwy||vw€y~~~}z{{~z…}~|€„~|„…„|ƒ‚€€€y~|‚z}~~€€…†„ˆ‹……~}{ˆŽ––‘…”Ž›‘‹—’“””Œ‘¢Œ†š“œœ¡«™š¥­¡–¨µ¶µ¨¹·¨¶º¸¬¦§º¼®®°µ»¼±«¶¼ÃÄÁ·¾¿ÀÉÉËÅ¿¸¨yqotswysuz|~~}{‡€€€z„{~zy}~~}zz}yx||{v{wxxuwuktsrsoqtplnpkjeq{ms{}ŠŒ–‹Ž’‘‘‘“”•”••”’—š•™™™˜˜šš™•—””’•—••“—–“–‘’”’’“‘Ž‘”Š‘‰ŒŽŽ‹‹‹ŠŒŽŽŽŽŠŒŒŽ‰ˆŠŒ™³ÂËÔ×ÜÝßßßááääâãÝ×Ì·“xhSB,(*.)-**$14)*./4653644475054633/.*+0875220.-*)6(*/.70,žžž ¡¦¤§¦¬­«ª¥¥ ž˜•‘‡‚|{{€~‡Š‘–š¡¥§¤¤¥¦¥¤¡¥§¦¨¤¨¤¢ŸŸ™–•…ˆ~xk^ZVMPNRVXS^[]cca^_^_cdd_fiedceebefhhongfikmlrtsqqwtuwyx{yx|z}~zzzzy|z|||~|{z{z€{||€||ˆ{|}{{}|y~|„€ƒ†…‘œ–Šyz~„‰—•ŒŽ˜Ž‘“ˆ”‹•‡žŸ””š•“’”™ž§Ÿ‘–©¦Ÿ¢©­¢¨²µ³®§¶¯¥®¼»²¥£·À½±¬¶»¼¼º»¹»ÃÄÈÈÅÁ¿ÄÆÃ´ž€qqpmvpt~|}{{€‚}~|z|y}‚|y|x}zw}{xzsrqouprrqtqpopvjmrnmkjnppzxƒ…ŠŒ“’”––’’‘‘“””—”“–—–˜˜–˜–––•”••’—“”“‘’’‘‘”—’““‘Ž“’Ž’‘‹‘Œ†‹‰Œ‹‹‰ŽŒŠŽŽŽŒŒˆ…ŠŒ¤·ÆÑÓÚÝßßßàáãäääâÚÓǦxX@3,*.'(,&,'.*/05999:8<25=69361355021,2077<:0//9-(+--.2..  ¢¢£¥£¨¨®­§ª¢¢ž›•—Ž…}|yvxx}‰Œ’˜š› ¡£¤¨¥¤§§¢¥£¥§ ¥¡  ›—˜’‡ˆ~ribXUNMNWUUX[a^`c\bbebdecihd_gbifcb_elnlmjjionqqswqttzvvx{{{yx€y}||~zt{|zxw{€~|{}{‚€||}‚€‚~‚|}~wxz|{wx‰™”•”‹‰zvtx|‹…Ž„ŠˆŠ˜•Œ•–ŠŒ—¡¢—Œš”“££ž¡ª§—§­¦¤¡§´±¯¬¬¥«±¹»ª¡´´¾¹°¬¼Ãù²º¾ÀÁÅ¿ÿÁÄÈÆÃþ©‰vrnmsu||z{z}}}‡{vxwx€|~|{{x~{|yzxxtvuussqopplosmllmjigilinpsqs{€~‚‰‰‘‘“””’‘Ž‘“‘™’”—““•’——™˜–•œ™˜—”•““””‘—•“’”“’—’‹ŽŒŽ‡ŒŠŽŒŠ‰Ž‰‹ŒŒŠ‹‹‡‰‰‰–¬ÀÎÓÖÛÝÝßàáâããäâßÙ͸„M/-.)**&.3'+)3.)+658911978116>3<=533:..06;34/.11+),/-.2-3¢¢£¡£§«©¯¯§ª¥ž¡š•—‹…|ysxutxƒŒ˜–šž£§¥¥¤¥¨¤¥¤¢ ¥¦¥¤£ŸžŸ——’†€rjcZUMNLMOWefZdec_a^_ceecbhdcfccdcccfjjfjjjklmtqnuryvutwuuy{wxyyw{{z|z|wxwxw}}„}~ƒ~~~€|‚€€}}{~{y„‚‰Ÿ¥Ÿ–˜“’†~~usy{}ˆ|‡‡‡…‡‹ˆˆƒŠ„‹ˆ— ˜‘’•šŽ“•™ª¢’—§¡¢°°¬œ ¬¥©°¬©¤ž«¸·¬«¬°¼³«°ºÂž¶±¾ÃÆÃ½º»ÀÆÇÃÈþ¾ÆÉƱ™nmmrwvyuvwwyxx~{xzxxxz~vy{{~|{{wxsrsqwvwsruorqnnoniiggkhmplsut}†‰Š‹–‘’””””“‘”‘’““𔑔”••™›š—™™––—‘‘•’Ž˜’“”‘’‘•’‘““‹ŽŽŒŽŽŽ‹ŽŠŒŠ‹‰ˆ‹‘‹ŽŽ•‹ŠˆŠ¡¸ÊÎÓØÜÝÞâããããääâß×»ƒC++'*+'),2+05+1*0.57567027644553,-.5//33.10-/1.:093-+(/05  ¥¡ª©ª¬¬¬¨¨¢¡œš™–Žˆ€xtpnhqx€‡Šš–›¥¢©¨¨£¦©¤¥¡¤¥¢ ¦¥¡žŸœ•І‚wj`YTPNRVV_c[]\`_dhjccaeeb_bfgbbdedchmfknljjmnppprkxr}pttxqwx|wyw{uxywyy~|yzx~z~{{|ƒ~~}}}}|}|~|y|Š®´š—£š–“‘ކ|z~}wy~x„†ˆ……†„ˆ…ƒ€ŠšŽ™ ˜••—˜•™¡—•§¯¤« ©®¨©­Ÿ›«¬¸²°®¶·°©¬»»À¿µ´ºÀ¾Â¾»¼¿ÃÇÉþ½Á¿ÅÈÊʺ¥„ponyyxqszwwwu}yvx}{tzzv{xz|y{x|zysvqtrprpvqqmoijmildfcikjnsuy€†„ŠŽ“’‘“’’“‘Œ”Ž’‘•“““‘’‘•’š—š•™š––›•—•“’’‘Ž’‘’’’•‘“‹ŽŒŒ‡‹ŽŠŽ‰ŒŽŽŒŒ‹‹‹Š‹“ªÂÇÏÔÚÜÝßàäåäçåâàÖ´p/"!)&($()-0.031).4:55230333543.0-.-0021M7425//,768/-.9328¢¢Ÿ¥¬§©ª««©¨¡Ÿœ—–‹‡zynjkelu~ˆˆ”—™œ Ÿª©¥¦£¦¥¥¢£§£¥¥¥¢ ¡œ™”ŽŒ€{pa]VYSJKLTZQVT\^\dafnja]dddcbg`eakfifgcjihkonopoqq~svry{wuz||wxyzyzzy}z|yzz€}~|‚}€|‚~…€|v|y€|wˆ‘§¬–—“ˆ‰„†~|…z{zw{}v†‰Š‡„|zƒ„‘’Œ…‹~›‘¤—“’˜ ­ªž˜¦ ¥´²¥¡¢§±©«ªº¼·°§¥·Á¿³²¶¼Â¸³µÀÁýÁ¾À¿ÅÈÇÆÂÄÅÉÆ·Ÿ‚jsttlutqquqxzy}xyvxzzwyxtwzyzzvvwvsuusurtppqplpnmkigggojppuu}ƒ‡†ˆ’’””•”‘‘’ŽŽ“””–•““‘˜—“•”——™šš•—‘‘ŽŽŽ‘“’‘”‘Ž‘ŽŒŽŒŽŒŒŽ“ŽŠŠ‡‡Ž“±ÂÉÐÖØÜÝàâääæçåß×¹p)%#-()*..-)03(-35489<66584==;1,/-.*,15236526,'*247*.,0721¡¡£§ª©«®«©¤¡žž–•’‡unecegs}‚†•›™Ÿ¡¤¦¦«¤¨¡¬§¦£¢¥¥¥£¥žž˜–’‰„xpdXWSJMORS\YWU^Y\_`acac_[cae`fd`ekhbcgfihjrktlnptussvzqq|uvyxzvysxyyyz{}{~{}~|{|{}{~}€{{}~|y|~‡~z‚‡…ƒŒ‹Š††„}v€}ƒyyuxxxyy‹ˆ‹‡…‡ƒ|y€ˆ†‹“…}Œ”š˜Ÿ•‘ ¤¤«¢£¨ £®›Ÿ°°­¦«¯µ¿ª£³¸¼¾µ±¯½ÀÀ·¨°½ÅÀÀº³»ÁÅÇÃÂÂÂÂÄÉÎÊÆÃ¸¥yrruoqpqx{vxvxuvw|{yuzwxtytwwwuwuvsssppqqikllnmeiefchioqqxyƒŠˆ‹Ž“•“•”’’’’•““•‘““‘”•“•˜“–™˜˜›”˜”’’‘‘ŒŒŽ‘’’’‘–”“‘Ž‘ŒŠŽ‘‘‹‹ŒŽŽŒŽŽŽŒŽŒ‹ŽŒ‰”©ºÇÏÖÚÜßâäåååçâÙÃ…8#'*(*+)/)*/01325389563564565..,-*.*219<040,/0++-./-+48993¤¤¦¦¦¬¬°¦ª¥¥žœ—•Ž€zrhg[\hr{„†•”™šŸŸ©©§¥§¥¥¥¢©£¦¨¤£§¤žš–”‹‡€slb[TNJSNTUVTZX^Y]ab]_`ab_ceacafaggcibghgidhinjrpnsqypuuspwxyu€z{rz|xx€x}xxy{{|}}}|}|zy}y{~’²˜y{€|}~‰‡€€}€{x€~}{|~}{~||„ˆ…„‚ƒ„€€†€“‹…ƒ‘„Ž•šŠš¡—•£¥ ›¥¥—££— °¥¬­¥§²·¶´´°±¼ÁÀ´·½½À·®¯¹¿Âø°º½ÀÄÅÁ»½ÄÇÉÊÊÇÂ½ÅÆÇ¹’lilpppqswvutuuxz|xzw|xwx}yzwyuuwttrqntqoohmjkhilj`cmmjoux€ƒ‡‹ˆ‘“–•–“‘‘’Ž‘”‘‘”•““—’“’‘’–™•”•“˜–Ÿ˜–“–ŽŒŒŽ‘‘Ž”ŒŽ‘•”“‘ŽŒ‹ŽŽŠŽ‘Ž’‘‘Ž‹ŒŽŠŽ—°¿ËÕÚÛÞâãæçäâÚ̦[$(('),,,*-2*'///6445893778561-/+-0-+2487310+,-,0.-20356<:4¦¦¨©®±¬­©©¡ žœ•”ŽŠzuiiZ\[dpx†–™œ¢Ÿ¦©¥¦¨¦£¦¤££ª£ª¥£¨¡Ÿ˜˜’„}vja^QQHKNKPQVVZ`\c^`_c_[`_heecbcddbfggkjghjkjmmhlnnvyswwqqyrzw{w{ty}zyx|}{xw}zz‚{|{}{z{~{{|~z}}‡¯Æoq}|{z‚{€||zvv}zzwy}y~†~…‹ƒ†ƒ€€…€‚‰Œ…ŠˆŠ‡”™“ŽŠšžžœŸ¨ š’“¥­–ž®³¶°¥¥¬·¸½¾¼»º¾¿µ±±·¼¿À»·À¼ÁÇÀ¹µ½ÀÂÈÇÆÀÿÃÃÄÌÉ÷ pjnjlopnqtttvzwrxxsyxvvuvwuvrsuusqplsnnlmpijmngidfjmpqtx†Œˆ”•”””‘‘’‘’”“•‘““’”“’’“’—š•š˜”•–‹‘’‹ŽŽŒ”Œ‘Ž‘“Ž•‰‹‹ŠŠŽ“‘Ž‘’–Ž‘ŒŽŠŠ‹Ÿ³ÉÐÖÛáãäååáÛÑ·z4"'*(#%'+,+32,44/486512/;=4583)..-.,.356380/-+).*/)24787421¤¤©®¯®­¬¨¨œŸ›–•“ˆƒyme]SQXcqy€ˆŒ”™Ÿ ¦¤¨£¨¥¥¨¦¥££¥¥¦¢£¢šš–ŽŒ‚€ul`YOJKJPUWPTVUXZ^`__^`^`bfa^jbcfg`hhgdihjiigpniksqmusu}rszwxrutwvs{}|{|z}{y|w}~€|~€~y€~{y|{z{˜±“ifm||{tzxvuv{zx}|v‚zzz{x…Œ‰‚†‡„†uyƒ‹‚Œ‚‹‹Š…’Š•—ˆŽ¢”’¥ –“ž¨§  ­µ²¢¡­°°·¶¹·»¾½º²ª¹½¾»´º¼Á¿»·®´ÂÄÃÂÁÀÀÀÃÃÉÇÆÄÂÁ¿°™mimlmoksutu€ruvvrwtvvwvvuwsxwrstptqurqqphljlejhidhlnnz‚„‡Œ‘’”–™˜••”’“Ž“‘‘“‘’‘“’‘““š—™š˜’‘Œˆ‹ŠŒ‘—“ŽŽŽŠŒŽŽŽŠ‹‹ŒŽ‘‘’Ž‘’ŽŽŒ’”¤¸ÏØÛßãääàÚÑ»‹D'&"***%))+.+2.127<=67644<5:2-,(+,))02.832/7//+-/).011731-($§§ª­°®ªª¦ ¡ š—’’‡ui_VQPXimxƒ„Ž•–ž¢©¦ª¦¦§¥¤¦§¦¡£¢£¥¥  ›—•„|tr_VONJGMQQUWZY[_]]^\c\ba`a_`ccbflaehbhnjhgdmfiiempnruqvssx{xsrqvxuzy{zy|z}y~y€y|€|~}yzzzw~zŒ¾¨m[Wjmxx}vyzp{}ysy}zyzu{zq~}‡Žƒ‚Ї…~}ƒ††‡Š‰ˆ‹Œ†‡ƒ•‘‘–¡–ž˜Š˜Ÿš›¥´º°¥¥¢¬¹³­®º»¿¼±±»½¾À¼·±¸Ã¿Á½±­µ¾ÁÇľ¾ÂÄÇÅÇÇÇÂÃÂÀÀÅÄÁº£}njhmhmjqos~rpspwstsu{wwurtwrospptllmpkokjkicgdbekmoq}‚‰‰‹’”•–—•š–•’‘“’“•‘‘‘Ž”‘•–——–˜”‘’ŽˆŠŒŽŽ’’‘“’‘Ž‹ŽŒŽŽŽŠ“’‹‘‘ŒŽ’‘–ªËÕØÞáãÞÙÓ¿™V1$,#&,,+**)+.6-395;928=784<865--/+/-32552.)/,//.1./877<6-'" ªª³¬°±¬«ª ž›•‘’Š‚~pf]YHO[frz…Œ•˜œž¦¤¤¨§§¤¨©¥§Ÿ¥¦¢ ¥£›“‘‰‰wneZRSJGNKOQUW\\a[]]_c__`bbafhccacc`gicdhbengjjfjktrrqsvprqsrusqvvw‚|~{x{|zy}|y{}{}z~y{{ttz}¡Ï©gb[ns~yx{ƒyxw~zvs€zxx|wvt}‚‚ƒ‡wzŠ|€………„†††‘‹€–“…†˜Š’“œž¥š™¤±¸­«³¶µ®ªµ¹¹º·¯©·¼¿Á¸¨¶ÀÄý³·º¿ÂÁ¿Á½½ÃÈÈÇÿÂÃÂÃįĽ¾ÄÃÀ©wfeajijomqorrmspqsotvstusutspooplmlolmlljkhidfhkrqyz{‡ŒŒ‹’‘”•–—”•”˜•’’“”Ž’“Œ’ŽŽŒŽ‹•’”•˜–“’ŒŽ‹ŒŽŒŒ’‘‘‘‘‘‘Œ’ŒŒ‰“ŽŽŽ‘‘‘Œ‘–”’” ºÌÔÜßÛ×Ó¿•W3*)+%,+*//.(-+420987;174;;74542('+-3335294/**.0*00876;86+'$¨¨¨®«®¦«¤š˜–‘Œ‡vmZPJKOVdow„‰‘—𦣢¦«¥©¥¥¡¦¤¥Ÿ¥¤¥¡¢ž™™’Ž…†~umcVUPHELVRSUZ_^\]d_ad_gab`bddfh_befhgbdedhejlkmoopqotsrqslryssvwtty}{wxt}wx|zz|w}y||}||~z|y€}{—±†i`fosuttw|}tw{v{|„‚yytysu{}„…‡‚y{‰~‡‡ˆ{ŠŒ“•‘‹‰™¡œŸ«ª¥¤¦²·²¬¥§··¸±«®º¹¼»²¶¹·¼ÅÀ¶´¹Â½¶º¿ÃÄÆÅÄ¿ÃÈÇÇżÁ¾ÃÇÈÈÄ»¢ƒmdfgjpijgjnmsostnsustrrrqvtpplonqojhmjknleigjgktrv€…†‹–•’——˜š˜˜”–•“’’““Ž”ŒŽ“ŽŒ‰Ž“–™“—‘‘”‹ŒŽŒŽ‘•“”“Ž‘“‹ŽŽŠ’ŽŒ‘”Ž‘“’”“•”–‘—®ÅÑÙÛ×Ï¿œb1,&&*-./),-+/,,152152/6:77648.,-/.-.12614,.,-)*0+-3:5836.3+,0,+:6/9?EJHD62, "4b««­ª¨§¢œ–”Š„}xm`STKKHQXcemx†Œ’—šŸ£¨©ªª©¨ª¦©¦¤£¢£¢ž¢œ–“Šƒ{yl^UMLHFHOQV\VY]d`df_gdg]_fbafdb^ag`cef`ehgfmkljmllumqnqwtvqtuvsuyvw}vywyzyyyzxz}}z{~yy|u{‹£ˆvjovpryvvqotlnourrwsvy}|||‚|u}‰Šys~„‹Š…„„ŠŒz|ˆ‚„’‡„‰–•ƒ„––“‰—£œ‘œ¨¬¤˜¡«µ´¦›¡³½½¸¸¯²¹¼ÀÀ¼À½¼ÁÆÅ¾¸¹ÀÅÇÅÁºÁÅÅÆÈÆÄ½¼ÃÅÈÇÊÈÂÂÈÌÊËÌËËõ¡p]_\_^\]ebdfilktolinopnlhiflollpnimfhfdehinvx|…‡““•™œ˜™˜šž˜˜š•””’ŽŠ‡ˆ†„‡‡„~€‚‚‚ˆˆŒ•”™•”–‘ŽŽ’‘‘’‘–’’‘ŽŽ‘‘Œ’ŽŽ’‘’Ž”’“‹“•–•””•••š›ž¤˜ˆnP43,/02'+-/(/401*14564472561,02.,+(,/6;:722-=3--006679@EAF<*&% $4^¯¯«¬§¢¡Ÿž’‹€yqdYPMMNNOYchtyŠŠ™› ¢¤§«¨§¦¨¦£¥¢¤£¦¤¡žž˜““‡‚~yn[VHPJHNSRWXVY]a[\]ca\ceffddjfe]fegaccbekhkliiompmtqsptpwtptqtrtxvw~xxxwy}{yx|}{y{~}|yyyzˆµµ}mooqtvprlmqjomqqpvuyvƒ€€‰zp~†‚yty‹‡ƒ‰‹‹†zx†ˆ„“~‘‘‚•‹€•žœŽ›¥¥’œ¡©¬ž £¨·¹·¬¯¶´º¼¼¹³´ÀÁÃÄ¿¾³½ÃÄÅü¾ÃÉÇÅÅÅÀÀÂÁÄÅÈÆÆÃÄÇÉÆÉÄÃÈÆÉËɘrnmkcZYa_eagheqlllppoonkjjlljejrghgggagdjomr‚‡“””—š˜š—š ž™˜š–“”–•—І†…ƒ~~€}z€}‚‡†Ž—š••’’’ŽŽ“–‘““”•“•ŠŒŽŽŠŒ‘”Ž’’‘“’š‘‘•”‘˜•”“•–™žœ›–ˆqQ;0./1.,'&0).')*035<=D:6133/12--*'))07569899<1/6414674=DCD80&" '3Vz”ªªª§¢¤œš‘…|tl\UNNPKMVU[hwx€‰‰‘ž›¤¡¨¨¦«¥§¨§«¤¢¦¥§¥¡¡ž—˜‘‘Œ€{qgbTMJGKLRSTYeX```ceefdaihfdchfgbehd`ebefddgkikolohsnqrtrspnrpsytv{yxvuyuxtzz~zw|~~|{|z{~—Æ©uqrprtrrtoqjhqtqvywwuv{xƒ€{n}‡}~xz€€x„~†’ŒŒ‹”‹ˆ‡ŽŽƒ†’•†‚ ›ŽŽš¡˜•¦® –›«®»·²²¨·º¼¾²­¼¾ÀÁÀÀ½¹ÀÃÄź½¾ÁÈÉËȾºÄÅÈÇý¿¿ÆÉÅÆÀº·µ»ÁÉÌÑÒ»­ª£šŠiYY^]`igonpmmrljginmjhjfjikhedadc`hlptv|‡‹“˜–”š™š™œ›ž¡œ›˜”–••“Šƒ…~x€wv{|~‚”–˜“’’Ž‘”“–’š“”“Ž“‹’ŽŽ‘’”Ž“’’’˜••’•”–”•˜–™–ššŸš‘{WA/.-*)'/*,4)&)*+0137547556/4.,,..(*/0353966/0--2/264@>CCG<30%""1Sp‹ ¬¬¦¥ ¤š˜ŽŽ~wmaVQMRXNPRU^kt|€†‰•–˜ ¤¦¦¨ª¥¥©¥§©£¢¡¨   œš•“މ€~sf]THMHINSQSVVTae]fef_^a`fadbfbececedggcejeohckiqkkkqrmsvuxouvvvx~xzvxoxuwr}wy{v{{x{||zvœ«ƒortuwnijnginmrntxvxxrvzxwt~„~{~}}„ƒ€ˆŒ{z„…†„‘‹’‰„…|Šƒ…Š‘¡˜“‘—œž¨Ÿ™—ª±´«¨¬´¸·»º¶µµ¼¿Á¾³´À¿ÂÄľ¸¼¼ÂÉÆÆ¿ÃÀÃÅÅÇû»¿ÂÅÆ¾·±µº¹ÁÅÍÑÔ×ÛÙÖÖÐÍÀžp^TX_`cglkjlodqlkgjffjghmlnhhegbeqmptt~…‹’’–—š™›š› ›™˜˜˜›—”Œ‡ƒ€xzwrwvwz‚ƒ†ˆ‘“–•—“““ޓޑ‘“—“˜”“‘‹Ž’‘‘’’’““‘Ž‘’“–“•–•™œ™ Ÿ•jG----+,-(50),')/156372529=5740--,+0,6758D632--,220175=CGB8/('$#(1Ko†—§§§¥¥¡ œ–“‹ƒ}we]PRoPONIPZbfrz‚†Š“™›¡¥£¦¦¦¦©ª©¥¦¤¦¡¢ ¤¡ š™–’ˆ€}rh_VOJMJLSPUWWX^_^\ldbbe_b_c`dfbfedfccfbkdfjekpimpplstpsxvwpuqorrzytvzu{z|v{}{yvvz{€ƒ{z}{Žªtptqrrjmnpkhlqsox|uuxqzw{r{ˆƒ|w~y€‚‹†}€ŠyЉ…ƒ…†{†’”u…Š›“‰’–“˜¢ ‹›¦¶µ¨§¬´¹··²²´¼Âúµ²µ»ÀÀÀ¼¶µ¿ÄÆÀ½ÁÿÈÇÆÄ¾¾¾ÄÃÂÀµµ°¼ÆÊÐÓÔרÝÛßâääãâÞË¢kTX[[eflnmjmkofiiebkhllggeffge`bhkmtw†ƒˆŽ’—™—œ›ž›š˜š•™™•”ŽŠ€ywurosvrv}€„‰Ž”™“”’“‘““‘’•—•’‘‘ޑޑŒ’‘’“‘ŽŽ‘Ž”“‘‘•”‘˜—™™™™žžž–ˆpV6-/(*00(((,)+(,239/3688861021**-/005<3:;313/501310468AMM@8*&&"$,Gc}• ¨¥¥¢¤žžœ”zm^RNPPUUUPQY[gwy‚‡’š™ž££¨©©¬©©¦«¦¦¦¤¡¤¡Ÿžœš‘‹ƒztp`ZPFJOPLV^XUY[_ahrdcfbciaj`afbidbbieeeelgjdjponprnvvrtrrsporuryrwxzxuysxzzz{zzx{uz}y{|}™¤~qpzomklnkholmouvtxtrsswtj~„zsˆ~vz}v‡‰€yƒˆ~„ƒ…ˆŒ‚Љw…’–ŒŠˆ‘“ŸŽ˜œ˜– £©­©œ©µ¹¾¾°°»º½Äò©¶¾Àýµ´½À¼ÂÉÆ¶µÁÅÆÈŽº¾ÂÂĽ·´¹¹ÀÆÑÒÒÔÕØØÙÛÛÛàããåãåÝÊ’\YYYY]ejgfjlmhffdffhhhijeheja`kjhomw‚‹•••”ž œœšœ››š–™œ•”‹‰ƒ€zvxjklpsou|~„‹Ž‘’™–•’‘‘“Ž’‘•”’‘ŽŒ’’‘’Ž’’“ŒŽ‘““’‘’”•”–—˜š˜˜žŸ—ŒwX<32)+/04-'*,9''+.211645453.2,,0,.+20;958876//-06204<99GOJ>7'%),1;[v‰š£¨§§¢ž›˜—Žˆ†yqhZQPXUWZTRJXagn|„•”¡¥ª¥¨§¨©¬«©¦¦£¤¡Ÿ¢š˜‘‚zth`WPJLJQTSXYWX^efbjb_g`ccfib]ffdbcggddfgffkkhirnsoyru{~qorurtoyqsp||ww|xu}}yy{|wy|{}{‚‰vvrqpnkmdgjupiq|w|yvusxqk{‚{}{„zwyƒ„ˆz‚…xwƒ‚„Œ‹†|‹‹„~†…ƒŽ‰‹Š–””Ž‹–’‰ž£ª¢œ¦®®¶´®²´¼ÁÁÁºµ¶µ¾ÀÀ»³³ºÀÃÇÆÂµ¸ÄÍÆÄ¾¹¼Á޽²¬µ½ÈÈÏÐÕÖ×ÕÖ×ÙÙØØØÜÞâãäæäÛ»zURSSZaaeiiieecgdfcecighehda^cfhlmlw}…‰Š’”—•–ž›œ›˜˜™™—–˜™—‘’ˆˆ|wsnfafighox{€‡ŒŒ••––””’’’“’“’•“ŽŽ’Ž’‘Ž’’‹ŽŽ’“’•–••”“——˜Ÿœ‘|[@9/.-+++--%&/2-.143254433413,*).*++608?:1-28/11.1.06:AHOVM;3)%%1>Wlƒ— £«¢¢£Ÿ˜—“އ~tn`_QMTSUVVXQW`kt~…‰‘˜šŸ¤¤¤©¦¥ª§§§¨¨¤¤¥£¡£žš•“‹~{rk]XVMKCLMTRSY\_^_`\b`[cacghdce^acffjefbffihlllhqonqlpsvwowypvu|zwy{xww~y}wz|vwwwz‚~|€Œ†|ztojojnnijpomosus~rpswro|{~}}x‡{w…ƒ|z~~x‰‡|ˆ„„”•x…‡†‚‡š™ŠŠ•’ ’•Ÿ©¤”­¯·´°ª²¾¿ÄÁ¸±­¼¼¿½¶°´À¿Á¿¿À»¿ÂÄÃÁ¸¶¾¾»·¶°·ÀÉÐÓÓÒÓÒÖÛ×ÔÔÒÖ×Ö×ÙÛßãâææãÕ¢bSMQV]adcihgbfhhfdfghcehef_fdjglrssƒˆ‰“”—œ™™ž˜›š™—™˜–‰‡xplg]aZcehry‚„ˆŽ‘—š–“”•Ž‹‘”“ŽŽ‘‘–‘”“Œ‘‹•‘‘’”‘˜–—–™ž˜Ž„mH.*.1,,-1)**/55.1-19=.12<042538.-)*/3497:00-2//.,1136>EKZRI=4+,1CZq…‘—Ÿ¦©žž¤š——’…„wqkYSPRSSTPTVTYcjr‚†—–›œ¤¢¥¥¨§§¦¥§«§¤§£¥£Ÿœ›˜•‹Š~xshZWPKKDKPUZW\[^Y^dabagf^\^bjeiafhgajfcbhhhjjmnqplnpnssrroosqzxzr|‚}z{{wywzzxzwv{w|~~„Œ~uukmphmmrmnpnxruw€|qspowzx{|x|rv~|t†‚€‚wz„ƒˆ‡‡{†‡ƒ}ŠŠ†|Š—¡~Œ’“•–¦¤Ž£©®©ª«µºÂ¾±¬·¸ºÁƶ«´¹»ÄÅ¿¹´¸ÂÄÆÆ¾¸·»½¼µ±·ÂÃÉÓÒ×ÓØÔÓÒÒØØÖÓÓÕÖ×ÚרÜàãâäáÝ»ƒZJSZ[\cadfhelfdchhffddaeg_^[afnnqt€„‡‰•”–™›œš™˜œœ™˜››šœ™–“†yqea^\\UYbomwƒˆ‹Œ“‘–“–’”ޑޑ’“’‘‘“Œ‘““””’•““’“””‘”—–‘™—ž›“oO6-*24(,9&,,%:+1.31:862264876/./.'+06A6<<71-(120++208>NZenw€…ƒŒ““““‘—Œ”•’—‘“’“ŽŽ““ŽŒ”Ž’•’”•œ•˜ššœž—‹tU6+*$**-())(,+..<84<6687067744..%*-3359<832),.+/.4443>=FORPC@8=K[jw†Œ–—“•–˜•¡••”Š€tncXOPUUVWY`WWTQ\\Yfqw€‹’˜š¡ ¤©©©§¨«¨©§§¦¥¦¢§š›–Œ†xsjaZUPLUKQW[X[\X\\[aaebafcafcegmhgeegdbefgfikikjjnqopqqtrruvssusuvwyxzy}wy{z{y}…z{}‚utiijfomirkrp{qyuqpsr~x|ysz{u|yxux}urozƒ|ƒ‡„ƒˆ~ˆ•‚ˆŽƒ‡ˆ‚Š“†‡’’‚’›§”•¥«ž­¬¼Äµ§¥±¼¼¿¶»³»ÂÁĹ¥ «©¥©µ¾ÉÍÏÐÛØÐÖÑÑÏÎÌÑÐÏÐÔÓÐÏÏÐÍÍÐÒÔÔÓËËÎÕ×Û×àáàÞààÒ¨mWJPOVVaZ]c`b_^_bjf^ba]bY`chppr}…‹’—š˜ ››š˜˜œ›šš›—š™—‘’ŽŠƒwfaOA599;BV]rp{~…ˆ‹“””””‘‘Ž’”–ŽŽ‘–’‹‘••Ž’‘Ž•‘‘’”—šž›€^8**$$&(+&)'%.)/-425?3B6859753,)&&.2465:9.0.)58,3;BMI61/+,%(,.-5DA?=FFCKUKKCQZk|”–™™–ŽŽ‹‚{reYUNUWWX[U[XXSVXU\_kp{Ž••–¡Ÿ¢¦¥¨¥¨©¦£¦§§¦¦¥ Ÿ›—“•‹‚ƒtqj_VTNKJMRXUV\[Yaaca`^f`aemeagemfgjdegagbgdkkopjoniovpwypsuwvutwzxvzxx|{~}{vyzz{…€€wmlspfofirlopsusvlpmztwyuwpsmq€{lstpqvwtz}|…ˆt†‡Ž…†ˆˆ{’‡‹‘—†…‹Œ–—šŽ„‘™™—‹ž£©¤˜¤¬°·¹²ª®¯¸¾À­¥ŸŸ¡›¥¹ÃÇËËÔ×ÔÔÔÏÎÏÎÏÎÌÏÊÇÏÐÒÏÍÍÏÎÑËÊÎÐÎÎÐÌÒÕÐÍÌÔÖÑÒ×àäßÜÞÙÅ”\JRMOUXWX\b_^^]^]_^a_be_gnqty†‰Ž“–˜™›˜››œ›œš˜•–•š—•””‘…ƒ}{mkSI@4.05;GW`mtw€…‰Ž””—˜•’‘Ž”‘”’‘Ž’’””•‘‹•‘‘‘“••˜œœ™€Z=25,'1)((,.+1.5;=:3590=46722/-0.++5<>;:432--3030>6:<<>AFBJPIJMXhs‰–™—•މˆˆˆ†}rc[YSSTWUZYWZZ[WVYUZakpz}‰“˜•›¤¥¥©¦ª¨¥¥§¦¨¥¥§£›–“Š‚€tok`ZPOIIMTX\Y_\^`_aa^]dibghf`kjhghbff`gebfikknojjkpnrqyrstuouwrzv~~}z{xys|yyvt{|‚{thokgjoijpkktoqtqkvy}zxquvqnv}woqsrmxz|vy{}‚ˆ{r}‹ˆ…‰’Š…‘‘‹‰’‘Ž‹ŽŒ’‹˜–š’Œ“‘“–›š¥¨—•¥¬²¼½¬­´²²À¿±Ÿž›˜ ®¶ÅÏÐÏÏÐØ×ÐÏÏËÉÈËÌÏÑÐÆÇÐÐÐÎÎÏÍÏÐËÉÎÐÑÎÊÌÏÒÑÑÕØÔÖÚÝâáÞÚÝØ°pTNOPTTZ[ZYYZfdbd_aa^_a_ehps}†‰Š‘•˜™œŸš›œš™—™™˜—˜•—•މ‰€|se]Q93)*-4CL\flqz†‰’•”——“‘‘‘ŽŠŒ’•‘“’‘‘”““˜‘——œš››~fF0*'<=IF<),*+-4;6?756689;0746/-3..,879=36/3,,-0-)7?9;::;0+,255375341667:70/,/-)1468:862-1-0840.8?8?BCJMJKCR\dpy†’”“•››˜…‰–›‚‚|tf^OTX[VZ]\ZV^ZRSZVXZ]hr}}„‘™—šž¢¥¦¥¤§¨«¤§¥¥¨¦¢¡ž ™•‰‡~{pcb\PLNGKTXTVY_a_a``^]cb]bhabccdgcgc_dahcekjjikkjnnktltutsusuvvxvxy}v}|zvxy|y{z€~tjkopghonqrzorrtqlvxvzxtutuvyulzxkdgtrs~‚{~ˆ}v‚†ƒŽŽ…ƒ‘”’ˆ†‹”Œ„†–’—Œ’•’•ŠŠˆ“–••ž ¦±·´­¨¶¶·³¬£§¡—©½ÆÉÌÌÈÉÍÏÎÌÇÌÎÊÊÏÑÖÓÎÏËÉÍÍÆÌÍÏÌÌÌÍËËÊÊÇËÑÐÔÏרÙÕÚÙÙÛàâäæáÝÜÛÁŽYKLNKVRSUXZXZUXYZ[]`]dhgtw|…‹”–ššœ ˜œš™˜œ›œ˜˜š˜’˜‘“Œ…|xg]N@0-)--:FTbdjsvƒ„Ž‘‘’•—˜–’Œ˜’‘Œ’‘’‹–’”“‘‘’“••—˜žŽvZ>+33+*1--0-/*.1/47238045499700+-,/3=8;30//0/4/472155<=I?EOKTQXcmyƒŒŽ‘™”––™”ˆ…Ž— {{xjcXVUTR][]a[VZ\[X[XTWYdo|€ƒ”“œ¡ ¥¢¥£¨©§ª¤¢£¡££¤¤ ž™—‹…yyoe_]TNHMQVXZ[\`b_adgc^[f^beaa`efdZcecdcjfhmihejinuqqtnyttvtvtvwzwsztw{zzvzxx}{‚tmokjmmonsmtoowoplvvvztszv{wtsssvoimuup~~z„„‡wƒ†…€ƒ“‘‡’Œ‹Š‘““ŽŠ•–““”‰Š‰ˆŽŠ‹—˜Š‘¡ ¬¬¦£§¶­º±¤œ˜—‘–±ÄÉÍÍÏÏÍÉÇÊÎËÅÆÇÍÓÓÔÖÕÎÏÐÊÈÊÇÆÊÆËÉÈÆÇÉÊÍÑÑÖØÚÐÔØÙÓÕØØØÞßßäààÝßÚ¾‚MHKMOOMUTUTZUX_\[Y[`agirv‹Œ–˜š›Ÿ›š—›˜——œ˜™™›’‹‡†~xj]J61)%,18:Saahkw|Œ‹’”–”““‘“‘Ž‘ŽŽ’“ŒŽ“’’“‘‘‘”—––Ÿ ˜‰dA*(0&*.'3-'**0*6436474.38420..-/-125:=DB..125:25/13/4B=CHJPQSWdpz…ŽŽ’”’•˜™˜Š…Œ–Ÿ£zznbVSQUVRYic[][\[WXSVTVccqu†‹’’™šœ¡£¥§ª¦§§¤ª¥¨§ ¡ œ›—Š…zljaWSOLUNQT\^]eacddfbfbf__hbc`hhg^`kfcb`gjlmggknknmywmupststs{uxvxwzy{{|}{z}|‚yuwqjljjor|rsnno{rrttyvrtttyrcousiopqnx}yv„‰…}w…‡€{ˆŽ‰„‘…‚”Ž“‘š”Ž‘š‡y…•‹‘‡Š“••Œ‘¡¤­§™£²¶°«¬  ™Œ‚ ªÁÍÏÏÍÐÐÐÍÉÄÈÉÌËËÌÙØÐÏÑÒÍÊÌÉÈÆÂÄÀÃÉÈËÉÇÑÐÛÖÚØÖÔÐ×ÖÕÏÓÕÕÚÛÛàääÞßãÖ¨`URKJHIQQVOXXW_ZZYW__fhqv‚€†’——˜œ›žš™œ™˜˜žœ›—›”’†€q`XN0&$,-16DPdafuyˆ‰ˆ‘”’•–—‘‘ŠŒŽ“‘‘’‘•’‘–’–˜šœ›~d?+)*&#)0*,,12137;25:78396551&-)%(-08<7;21),+4003301:5=7;@JJP[hrzƒ–•š”’‘”‘ˆ‰“š £££kkfUPSS[XVZYZX][Z[VZWPXT^dow‚†Œ‘’–Ÿž¢£¦£¤¦©¨§§¥¤¤¥¢žšŠwrlwYRHGPWV^YX__`eY^bheh\cadcbf`dafkhcfhaejgbeeilnpotmquvzyyprttuuwywzywzy||}ƒvuxssnkrzokuvqqvlpstuxzty|xqrstonqrrsy|wv{„|s‹Œ}zxˆ‹‚‰Š‹†z„‘•’„”’“”“—”ˆ„€Š‹‘…•”‡œž™”›¨­±µ¥›‘‰‹ž¬½ËÉÈÎÎËÌÉËËÊËÉÈÈÏÓÐÉÇÏÑÍÇÆÈÉÂÃÈÍÌÄÉÎÆÑÓÒÊÎÍÔÑÏÌÉÏÇÊËÌÍÏÍÍÐÔÓÓÖØÙÙÜßÖ¸žz`VQPLJQMMUXT]YSSV^ajpu|‚ˆ‰Œ’•—››™šœ›ž˜–›–›•——”ŽŠ~tcXK;/'/@5,+;P]`jjt…„“—“”“‘“ŒŽŽŒ‹Ž•’“‘•’“™”š›œ”†lK96-&#%%+0+,2/2;686??631942,--+&%1159<<3-++285450578@H;=7@FOUctz€Œ•’•—–‘ŒŒŽ”˜Ÿ¡¤¥cc[UTWWSVXZ_][_[]]WVSR[\[ekz~…ŠŒ’—šŸ¢¢©¤¦©¨¦¨§¨£¥£¨£ŸŸ–™‹ˆƒ|nkaYPOKOPLS^]W_baaecdagZhddebc^gh`cbddifjigjihkmjroqptr}zssystyuwwwyw|x{wu~‚‡{rsztovonzkioqospkoystztpxoinrql}zq{€ztt‚y||y†{yƒƒˆ}ŽŽŠv‡‘‹Š|Ž™‹‘””’‡‹›ŠŒ‘“‰‹™”‘¥¬©²©š™••¦¿¾»ÊÎËÉÊÇÉÊËÌÍËÍÌÌÉÏÏÇÂÅÊÈÆÁ»ÄÅÄÅÎÓÍÊÓÆÌÓÍÈÈÄÇÆÄÄÄÉÐÍÌÏÏÓÏÓÒÔ×ÖÓÔÖÚÝßÞÙθš~jULGKJKLMTS[YV\a_im|}‚†‹Ž“”˜œ™›œš›˜œ–›™™™›˜—“–”‰‚tdVL;1-*#)9C?;791,,-.6557;:;FRcl€ˆŽ’”•˜•–“‡‡‘›šŸ¢¤¢¥aaWQUWXUXY\`\`[\Z^[XVYUYhgkw€„ˆ“˜š ¡¢¤¤¤©¥§§©§¤£¡¤ŸŸž˜”Œ‡ƒ~{oiaZSNLKNMNXX_^`_aadh`jbfdgabdd`dcchgicnhigjiqjgmolttr{v}twsusq{u|y}zw|wyz‰‡xoqrprtyvonnoqlpqqootvor{tkp}qwq{xk}rm}zw€Š…{‰‰„zxЉ“…~ˆ‘‹ƒ|…–„–”Œ…‘ŒŠ‘Ž—–™‹˜§«¯¤“Š{y›¯·ÄÇÁÆËÎÉÉÆÁÅËÎÏÐÑÎÊÄÃËËÉÅÿÁÅÁÁÆÌÆÎÔÐÉÍÄÄËÆÅÃÆÆÇÊÈÊÉÍÒÐÓÑÒÔÒÓÑÕÔÓÖØ×ÛÜßãâÝ͵’aGDCHJLIWWSWUW]beow}ƒ„Š•–›ž™œ››œ£›˜šš™—š˜“–”‰‚|rdYS73*-(-=;31=O]`hzy|ƒ‰‘“•“““’“ŽŽ‹ŽŠ’‘’–‘“”™š™›”€g=')%))%(('022.=D777656937291-./+1-17?994/0**38924943887:==FFXgx‚ˆ‘“•š—–‹‰“šž¢©Ÿ£¥£ZZTSTZTY[XW^]a[ZY`[ZUVSW\kkxz‚‡Ž™”™ž¡ ¢§¦§¥£§£§¡£¤¢ŸŸŸ™–‰†~yqja`SJHKJNLT_V\`_e]b_bbd`cha`da^cccadeahblhkhejllqmlrqvusqrzpsoyttvw~{yy~~ˆŽxwojslmmrwuuqpmpmsputruwzuvrryx}x|y|vuwyxrz‰Š…x}…Šy|ƒ†Œ’ˆŽ”„„‹’“‹†ŽŠ‚‹‚z‡Žˆ†Œ›‘‡‹š›©ª œœŠ{ƒ¢»¿¿¼ÈžÂÊËÉÊÄÁÌÐÑÑÑÏÊÆÁÃÊÏËȾ¾ÇÍÿÎÇÍÒÐÈÆÁÆÉÄÅÉÏÑËËÎÈÌÏÑÓÓÒÐÔÔÓÔÑÔÔÕ×ÖØÜÛÝâáßÚÂ’_NRNDGLSMTNRWbahnv|‰”“”›š™™—œ›œœ™—™™™œž—‘‰‚|qdXS;3.-,.*#,03?VYblv{ƒˆŽ•˜—““‘•Ž’ŒŒŽŒ‘“’’”——›™œœ“†kK8,.-%*%')',1-:3:;>7::977750/*)$.-12;><50+0,1014:47/77:@EHMQRQVX\agkr|~†‹Ž“š—˜ž™œœ—ž™—š˜–›œœª™‘Ї‚ymfYT>0(*&',+(21>FP]erw}€†ˆŠ“–“”’’“‘ŽŽ‹Ž‘•Š“‘“•–•˜™œ›–‡uX2*%+(%(&)/+++1=724;7D:8822497++&/28B::60-+1:/,;76=267<;?ACHPZm}Šˆ‘“’—™™Ž‹”Ÿ¢£¤¦¥£¢§ZZUUUZYY\ZZZZZ[\_YYVTXWZ`dquƒ“—šŸ ¤¤¦ ¥¤¤¨¥£¦¦ ¤¤ žš•ކ|yrlb]SPKMIMVYYUZb\`^Zegeabd``ffeaceh_fhlpefdgihkmonnoqqsxrvtsvuwtuuy~tuv|x‰‹‚tpjijninntyypnwsponommostzqrpvtpz{ou|sqzŠ‚ˆ~Ž|z€„Œ„„‹Žƒ„ˆ‹‡’–‹ŠŽˆ‰Ž„‚‡…‰†Ž–•–‘™ž•Œ}€š¯½ÆÈÇľ¼¾ÈľÁËÏÊÅÁÅÊÊÇÈÈÆÅÆÃÂÊÎËÄÆÒǹÁÅÅÂÁËÏÏÏÌÎÍÌÎÍÐÎÏÐÍÏÏÌÒÓÐÐÏÒÓÓÓÓÔÖר×ÚÚÛßàááÜÅœxUCFHIHORNTV_gntz|ˆŠ”•˜™›œœššž™™—™——ž—›šŠˆƒxrjYOA5=22+)#*+54=OWbht}€…‡ŠŽ”’”•‘“Š‹‹‘ŽŽ‘‘”–”•–™–›˜’~`?-%#&')+%(-,.-//6<;9878857735--+-.178663-.231./:545?;0;3:9>P]k{†Œ’‘ŽŽ“•—“ŒŒ•ž¤¥¤¥¤¤¤¤£YY[QS\Y\`\YaZ^]Z^_\WVXXZ]fot}„Œ‘’—žž¢¦¥¦¦¦¦¨¨¬§©¨£¢ Ÿœ™–“Š„~~roc_WNJHMOSVV\\_`_afc_cc_^cabffabddeghgachheenlmlqpoqvvwuvyuwvxuyv{zwyxz€‚{tkotpfjopnrtrotnqqnuqipsqmqsutwsqvy~wq|„‡‹ƒˆ†‡xzˆŠ……•”…€‘“‰Š˜‹…‰‹Š‡ƒ…Š…~€‹Š’„„’“Œ‚ƒ”•…‚‡§³¿ÈÅÆÇÄÄ÷ÂËÅÁ½ÃÈÆÃÃÂÇÇÅÇÃÅÅÆÇÈÎËÄ»ÆÅÂÆÍÎÊÃÅÎÐÒÑÍÏÎÌÍÐÏÍÒÑÐÐÐÎÒÑÑÑÐÔÓÖÎÏÔÖÖÔØÔÔÙÜÜáæÜÅ—aA@DCGIONVX]aks€„‰Š‘–™›œ›œ™›š›˜•—š˜¢˜š”Ž‹ˆ~xmf\PA4-+1*($(+.08FWZjhv€„Š•”–’–“’‘‹‹ŒŽ‘‘”˜••–˜››—“‰qH-3(#)%++'+)7/5/5B87935078:60/3.2.6::@G;6.,.9=146353742688>M[qz…’‘Ž‘““Œ’šž¢¥¦¤¨¤¤£¢ZZWYW_]_Z]\Y][b[VX]XUZ[\bgms€†‰‘”•™ž¤¦¤©¥¥¦¦ª¥¤¨£¤¢Ÿž›”‹…~|nlbcWNIFLRQVRZ\[`[^cd^decifegeghbfcdgch`dgdgjiknrnnpsuruuusyxtzytsstwxy’‰xvkjrojhdimnvvvrnqxpopsuwriqqovvrly|qvv‚‚………yzŒ‰Ž‰“”ŒŠˆ‹“–ˆ‚‰“‹|„Ž‚ŠŽŠ||ˆŽ‰‘u€‡{„’Ÿ»Â¿ÀÃÅÇÃÇÇÁ¹ÍÊļ¼ºÃÄ¿¿¿¿ÄÄÆÅËÈÉÆÈÁ¾ÄÉÉÉÏÓÑËÁÄÌÓÓÎÏÒÍÍÍÎÒÎÑÓÎÐËÎÏÐÐÎÓÒÐËÉÓÔÕÔÔÑÐÔØÕÝäãÙÃ}L=B56?FGLSYYems|„…‹’———šš›šœ™™™›˜˜™˜œš˜“’Œˆ€vqeZOF3/,(++%&&-)/5:LX^i{yƒ…‹“’‘•‹ŒˆŽ‰ŒŒ‹’•”””—›—“‚c@.-,')-.0*)--5.2294=75/346H4400,3144<23-,**/0142?@7403333;FUgz„‘–“’ŽŒˆ†ŠŒ”›Ÿ¡¥¦¥£¥£§¡£UUVUP^[^_[_]Y\]Y^[VZWW^^`eu}„ˆ““›Ÿœ¤¢¦§¦§¡¦£¦¢¢¡£Ÿž˜—“…€ynh\^ZMLOHKRR\]]a[a_cae]c_hdljhdd`cbebbdldbgfgpjlomlsoosutwtzyvzwuoossr¼š|ree^mjfbklckpossusxmjtoksprtxtlmwrwv|wvx~Š„~|…Š…‡Ž—ކŒ’މ…†Ž•‰†“’Ž‚‰‰…„‡Š‡yƒ‹‰‚‚„ƒ®°³²´·ÄÀÂÉÊÇÆÆÈÁ·¹Åǽºº¿ÅÃÿ¾ÇÄÀ»»ÂÇÇÎÐÎÍÎÍÃÈÑÒÐÍÆÆÏÓÎÍÓÒÎÍËÐËÐÐËËÊÓÏÓÐÖÑÒÑÏÑÓÚ×Ö×ÖØÔÔÚÛ×ÜâÖ§[;96>CKXanv€€ˆ‡”˜™••˜˜—™˜š–—›Ÿœ› —„}qlcRD>83-,)*/0+,&1*%(%(-5COUd…¦ÃÃÉÒÚÚÛÙÐꜬ´ÇÅ«•’‰r\6,+''+/-*:453/7608:6721.8489=84/901;846,-.775=4172251.'(-CKS_ip~‚…‰‘‘š™–•˜•˜œ›—›š–—œ˜›œ—”Œ‡~vg]R??2+//-2.(0+),&()#(..>Ni›¿ÉÉÓÙÞÚÖÕÕÏÉ¿¹ÇÌÓÍÁ©“Y@/.-.**,1/1.)116828489678;AB;:71<9540.6/,1/0398<920-./,,-8Mar…“œ——Œ‡‰‰‰–›¤¡¡¡¡¡£¡Ÿž¢ Ÿ ž¡ZZac[X[[\YY\VWYUWTVVSUSW\flw‚ƒ‹”——œŸ¡¥¤¤¦¤¢¤ ¥¡¦ ¤¨¢¤š”“ŠŽ~|mh^XSFPKRRRY]^Y``^^\[abf_hlbddibeb^bejiixqojmkkihokqnootrooqrlkjj„ÅÓ¦—sebbnnjdhjjmnmsmgptpsvnillldp}{usu€}w|†„z‚‡‰€ƒ„‘ŽŠˆƒ‰Œ‚vŽ‹‰ŠŽ}}tn†€~ytmkr†›Ÿ¤¬º²¹½½Â¶°ºÃ´³´¾½¼¼½¿¿·²´¨¢©¶µºÅ¿¿ÂÄÃÆÉÅÄÁÃËÉÇžÂÌÊÊÅÌÎÍÌÈËÆÉÊÍÈÇÍÏËÈÄÅÄÇËËÉÈÌËËÌÏÏÌÌÒÕÓÑÌÊÊÑÕÕÏÑÔÖ×ÖÙáâáãâßßÄh34CJ[^km}ƒŠŽ“—˜–•—˜•›š™œ›˜™™˜˜™›’‹Œwc_UH953()-0.-+.-&"&#$#&2Iq®ÆÍÓÚáÛØÓÏÍÃÂÆÍÓÚ×ÚÛØÏ°qF/*+,,3*)7A6*33=;:65753496:>CB8422;85:3.0--3626<7710,++.7GP[hq{‡Š‘–˜˜—™Ÿ˜››—››šœž˜˜››’Œ‹}olgRG:/2.-/0-.((*(&3&'-F…¼ÓÕÜÝÚÕÏÄ»¸ÁÍÐÐÑÕØÙÖÙÙÚÚÒ¸P"#%(/+1-014*195>A=4:89>/18164/=59779<44//42<6E>5.+'%*--?XiyŠ‘”™–•‘‹‡’™œœ¡¢§¡Ÿ¡Ÿ¡¡ ¢ ŸžŸ VVXYXV]]ZYZZY]\^\b]TTOVQWbkuy‚Œ‹’™š›¡¢¥¦¥¥¤ ¦¤¦¤£¤¡¢Ÿ¡—˜ŒŠxsga\PFBCJPORSVZZUXX[`^]__^a\`b`_a^_dbe_fimhfhekgjjoikmlolppjiebdc‹×ØÉ¥’{mf`fmiedgmmnnrlpx|qgonuqwtoiu{ztnizƒ|~‰„ˆ”ŽŒ…~~‚‘“ˆv|ƒ‹‡Š‡‘Š}xnoyzprnsqvŠ™ž«®³±²¬¯ÁÉÁºº±¹¬ªª«°²¹ÁÆ»­¯°¹¹»ÅöµÁ¾¼½ÅÇÃÄÄÄÇÈĹ»ÈËÅÅÀÂÄÈÆÆÂÆÎÌÉÆÃÀ¿ÀÂÄÍÐÎÑÍÉÊÏÐÐÎÈÃÈËÌÎÏÎÏÎÑÒÐÑÎÒÒÑÔÑÒÑÓÕÖÖÞäÞààØÃx?=HLYanv‰ˆ’•˜š–—™˜šš›ššœœ——œ•––ˆynkaVI;2*/).(*(%%$'!%(.O—ÀÎÙÚÜÕÐÉ·¶¿ÇÎÐÒÕÛßßââââÚÓÔÈp&&*0,-(0/./1247774703AAM83,3415646683520/57D9465162=7>97:;2333074;5003/87493750-')*.DWo€‹”™””‘‘Š‹Œ”™Ÿ¦Ÿ§£¨¢ ¡ Ÿžœ¡ž œ›ž Ÿ __WY\YXVYVYZZ\ZYXZYVUVUXWahs}……‹‘—œž¡£¤¥¢££¥£¢£¤££§¦¥Ÿ™”އ…zth_TQEJENIQMYRTZT^[_^``faab_^a^b_^Zb^ededhlhimkljjhjjltjnoslhggmyÃÚ̶§Ž}ohd]`jfjkirqhotkkmxursvtuuyswsy€{s|z€ƒ„‚{…†‡†‚„Šxtz††ˆˆˆŠ†}y|„ƒvmdkip‡ Ÿ«©¤¦®»»¾·¦¶¿·°ª¬±²³´´³°°ž¡¬¸¹ºÃÁ½·¶¿¼³²¹ÂÃÂÆÃÀÀÂÄÆÇÀ»ÁÆÇ¾ÀÄÁÅÈÅÆÂ¼¾»ÂÄÉÈÈÉÈÌÌÏÎÐÉÉËÏÏÍÎÉÊÈÍÍÏÐÏÌÌÏÐÒÐÏËÊÏÒÒÏÓÓÖØÙàâãߨ¿sBCNVbjq€†„Ž–“•—š˜–™›™—˜—žœ˜–––•Œ‰wmb\NC:1.++))'""(&)+@x±ÇÒÚßÚÑ«°ÀÏÓÐ×Ýàââãàääåçèçæãß¾O"*-).',6.+/;7<=91415=73:122.3483820.-:3=.77666<,1'++=Lj{Ž“›˜˜–ŽŒŒŽ–™Ÿ¨ £££¢Ÿ¡Ÿ¢¡ŸŸŸŸž¡›œWWY^YU[ZZ_]^W\Z^\]^]WYPT[dos|††Ž‘•š Ÿ £¥¦¥¢¦¤¥£££¢¤£ŸŸš•Ž…xyrk]ONLFEIINLORXZ\[[_^cc`bccadcee]`bgfbfe`lggfemkjmjoonooslkoifdk’ØØÄ³ªœ„ria_gikkfimqoohgstvqqzpmt}{prw~zvs…„|{~††ˆƒyŒ‹†wnjw†‰‰†…‡{x†‹~vklkipŠ¢¬¯¦­ª°³À½°ª¥©±ºµ¯¬¨­¯¸¿·ž™¢©«¾¾¼¾½½³´¶À¿»¶¼ÂÅÄÄ¿ÁÄÄÃÅÅ¿½ÀÅÆ¾½ÃÃÀÁÁº´¿ÅÆËÌËÉËÈÊÌÊÐÏÉÈÈÈËÏÏÌËÆÆÊÍÍÏÎÎÏÑÎÏÎËÊÊÌÍÌÎÑÓÙ×ÝäáßÛÍ ]?AXZmt{ƒ‡’’–••œ˜–—œ™œ˜š—™š™™—’’‰‚yod]SC4-.&*'(3%"$(1MйÌÔÝÝÕ͸¡§ÄÑÓ×ÚÝÞßâàßàßáâäåêêèäÔ~*)/),2.+,00/499477:6483621...520820,3418:241>62-+*=CKguƒ™˜”—”’ŒŠŽ’˜¡¢¢££¤  Ÿ Ÿ  ž¡œ ŸœœŸWWUXW][Y]]_]^_]b__ZXX[TYZbmtz„†–™¡¢£¨£¨¨¥¥¢§££¦¢¤¡œ›‘„‚~tj]XNDBIJLNSRTVW^Z][c`baab`abdceabecaaah`dgmglfjijknmtqsnnlpkbcl ×Ðí« rfghbkddkmrorkclvvtrlrvuwruwxy}qq{Šƒ}~ƒŠ†€zxw…†~ser‡‡ŒŒƒŠ‡|x|…{qjolfvŽš¦¦®°¯¶µº»»­¢¢¤£¬´°²²·µ¹¬¡ ©±°±µ¸¼±·¿º»¼»¾Â¼º¾Æ½Â½¿ÉÿÂÄÅù½Çý½Â»¶¸¼ÁÆÈÊÍÐËÉÉËËÉËÑÐÊÉÉÄÃÇÏÎÌÇÁÇËÌÍÎÏÊÍÍËÍÌÊÈÉËÎÏÐÐÕÙÛàáàÝÙ¾€MGKTjr€ƒˆ‘™›—š™ššž™™Ÿœ››™™›š–’…~wkh^UD03,+$)!%%'(2aœ¼ÎÔÛÙÓÆ«Ÿ°ÉÑÓÙÞÙÝÜÞàßÞÜÝÝßáâåêêåÛ 4(++()-/+210125;666:23//+02/9.63/2/,/.:4;?5:-5-0/32EWq…Œ•›™™“’‘…Œ’’˜›ž¡Ÿž¢Ÿ¡ ¢ Ÿ¥¡    ŸŸ š ZZUX[XU[ZZYZZ_^Z``_UZZZX]ciw~…Š”–™œ¢¥¢¥©¤©¨¨¨§¥£¦£ ¡˜”Œ…„|ueYVMJBDIGKPOPYZ]__[^^fbbhcbcibac`ccdhc_feficligiijiqsroophjieh{¬ÕÒÀ²§†wjcjade`iaiplegjow~snlnxvnky|xkpn€~„ƒƒ|un|ƒ€qk]u†‰Œ„‰ƒ|€‚€zronljx•¡£Ÿ§¬º¸´´¶³­©¡Ÿ¢§£¦³¸Á¿´¦¢­®²·º´«²º´´º¾½¿½¸¿ÀÀºÄŵ¿ÉÅÂÂÅÆÀ¾¾ÄÁº·¶¶ÁÅËËÇÉÉÎÌÈÉÊÍÊËÎÊÈÈÉÇÅÂÇÉËÆÄÆÇÏÌÐÏËÊÎÊÌÐÍÌËËÓÓÐÎÎÕØÝâáÞÝÓ²iLKVhp|‚‹‹’š¡–˜œšžœ˜›™˜™™–™Œ…{oiZH8+''(!$#%$.Dl²ÇÑÖ×ÖÍ»¡¸ÏÓÓÙÚÜÛØÚÚÚÛÙØÜßÝÞàâêéçß¶B'+*-,*)-5./16239342.2-0-++,/)*62,8513037**%"&97Jg‘™™™›“‘‘šš¡  ¢£ŸŸž žŸžŸŸœœ Ÿžšœœš›œ¢œ›___e^c`bcc^decbe`ec_a^^]\dpq|ƒ’“˜Ÿ ¤¨¨¨¨¨¦¨§¤¥¦¤¤¨£¢Ÿš•‹…zpkeXTVINPTTQXY\a]`\bcfedghdgaacnehagfeggfkkfhimopnmoqqrqplkefq¶ÙÔÈÇ«†wuhcdfid^lkigjfZ`kuxzytvow~„}vz~zvtq{ƒ~zxm{„…‚…„–Œ‚‚†‚nabi“š–•œ«²®§¤—¡¢Ÿ›£¤µº¬œ¡¬«´¾·¶±³¶¯·µ®²µ»¹³¶ÁÀ³¸º·¹°¯ºÀµ²·Áµª¨«½»´¾Ã½´¶ÀÂÀÂÇËÆÃÄÇÃÃÃÀ¿ÅÅÅÄÅÈÄÀÁÈÉÌËÈÇÀ¿¶º¶¥¯ÁÉÊÅÆÃÄÌÌÌÊËÌÌËÆÍÎËÍÍËÐÍÌÍÒÔ××ÔØÖÍÀž†„†”•š–˜œ–š™››š˜™•‘“„|wfZPDKa”¸ÉÒÜØÏÁ¸°«ª½ËÏÔ×ÜØÖÔÐÓÒÏÑÍÏÊÈÊÊÍÌÍËÍÐÓÕ×ÛÝÝáââàÓ†((%)).6663877:8)222*.,.,,)*,-15282631/($!!%-GZwŽ—™œ˜•–‘“–›Ÿ ¡¢¤¢ŸŸž¢žœ ¡¢›››œ›—•˜šœŸ›^^e`cca^bbflcbbbcab\a``^_glr~„‚‘‘—›ž¢©§¦¥¥¦ªªª¥¥§¨¨¥¤œ–›”ˆzolcWTNJLPMSQZ[_a\\bbdfbaajgcgbfjgqghgdeefgmmklkpmlpqqourpigecwÎÛËÆÉž”}gn[ba_ccgejhc^ilqu}wrqwzrqtsz~|wigpzy{yw{ƒ†ˆƒ„…~{ƒzn\[p}šŸ£ ”˜Ÿ±µ«žŽ”›œŸ¨´»­‘¤¬®²¬³¾º°³®®·º¼·º©´³³¸À¿·µµ¼¹²®¸¾¸¯±´£©±¾Ã½½À¾¹µ¶¸ÀÃÂÃÄÄÂÇÆÈÆÄÄÂÃÅÁÅÈÇýÃËÉÍËÇ¿¶©™§­ÀÆÊÊÈÉÅÅÇÍÌÉÉÊËÉÉÈÊÊÌÍÌËËÈÌÍÏÕÔÕÕØÙÖ¡‰…“–“˜–™™—›šœ˜––•”ŒŒ„xqdYV[w¥ÁÓÝÕÏÊ¿°µ®®ÀÌÑÖ×ÕÖÖÒÏÏÒÓÐÎÌÌÉÉÈÊÊËÌÌÐÔÕÖ×ÚÛßãâáßÑ|'0(-22,11346<870/5321/00/*),707>268>0( # $)AUk†•œš––“‘’›¡Ÿ¢£¢¢  ŸœŸžœŸœžš›žŸžŸ™™›œ››¡cce`cb]edcccdc`eaa`^e]X\bfjr~„‡Œ‘•œ™¤¤¢¨¤¨¤©¥¥©ªª¬©§©¤ž›—Šysk`XUOJOQQPVXZba^]edhfbdfhlildghemhjgafejfmnnrpkmootvqttomjddƒÐØÊÐÖ’„xrle]\adabgda^lpnqryulr}snn~…€vofq€}{xx|‚„{Œ‚„}zu}{„ug\_d{”œ¨¢¢žš ­¦ž”•’œ°¶®£“Ÿ­­¬®®««°»¸®°­º½¿º±¨ª°²µ³¾½±®°¶¸´±¹¿¬š ©­µ°·»Áÿ¾¶¹´µ¹ÁÀ¾¼ÃÆÇÅÂÅÀÅÃÇÄÄÆÄÊÇÃÂÊËËÍɺ ™›³ÅÅÉÌÆÊÉÆÉÈËÌÉÅÉÊËÊÈÇÉÊÊÉÇÉÈÍËÌÑÔÖÒÕÚÚÕÁˆ‘——™˜˜˜™˜š”˜š””‰‰~tq`drºÍ×ÛÕË¿²À¹±³ÅÊÐ×ÙÖÓÓÓÐÏÏÎÍÍËÊËÉÅÈÈËËËÏÐÔÖÙØÚÛÞáãßÝÍm',)*112344354F110330.-4.7-17978969;40,# %1Lc–—›˜–”Œ•šŸ¢Ÿ  Ÿ¡ œ›ž›šŸž›žš™›žœ›ššš™œccacddecdcdbgfbda`a`_ba^bfjoyƒ‰‹—”𢢤§¨¥§¨ª©§£¨©ª¨¥£Ÿ›”’„{rj`XONKOPRQ[TVZ`^a_d_dbffehcfijfkikdfccddhhirimlqltolwosqjjhgÕÒÐα“wwofe]]c^dhhaennnproqnqvrj`kx„uqej{~€vv~€ƒ|v†{sqz€…|mf`p}˜˜Ÿ¦Ÿ§¦–Ÿœ‘—¢´²˜’—¢³­·®«ªª«ª°¶ºµµ¶¼¿¿²©­¬¯¶±±¿ºµ­´·µ¶®­£˜¨°·´µ²°¶¿Á¾¸¼¼µ±¾Â¾ÀÁÄÅÃÆÄ¿»ÇÈÊÈÅÃÅÇËÃÇËÉÆÄ®˜Ÿ©·ÄÃÃÈÇÊÈÇÉÊÍÌÈÆÉÉÌÊËÊÈÇÇÄÂÇÆÌÊÊÌÐÑÔÓÙ×Ùλ—‘•™››™–˜™˜–”˜”ŒŒ††vpw’¶ÇÓÝ×ÏȾ¶½¿²¼ÆÎÑÖÕÔÕÒÒÑÐËÊÍÏÌÊÉÉÈÈÉÍÍÊÎÏÐÒÕÙÛÛÜÝàáÞÝÊZ-/*'&-.1./..4>68B765023/.51312-74184,'" ",:]v„‘œœ™–”“ššž¡¡¡¢Ÿ¡¡Ÿ›žŸ¤›™Ÿ›Ÿœœš™œžžž˜˜ž›bbaccdbj`efcdcdjcdj_]]`c\ejq~€ˆŒ“ššŸ¡¥¤§¨§¦¦¨§¤¥«¦«¥¤ž–•}tjb[VLJROTPVSTW`^_\ebdbdbbebgecikkbjeedcdehorklmoopqmsqqrmdfi«ßÓÕȶ¡{utstf`bX[c`dirnquskfjqyk`\g|vtuhiw|}~‚ƒ|…wqmpxy{wjbfo‚˜›”›™£¤Ÿ ’Ž˜›––£§–“™¨ª´³¯¬­©¯­¯«©¿¿º¯ªº¿·®²­§°µ´°¸¼±¶³µ²©”ž±¶·¼¹¶´³±¹ÀÀ¾¼º¹µ»¼½Ä¾¼ÁÃÅÃÁ½ÃÈÌÊÅÅÂÆÍÄÆÊŰ©š§±¹»»¾ÃÄÆÆÉÊÉÉËÍÉÉÊÌËÊÊÈÈÅÅÅÄÄÂÈÈÌÉÌÏÑÑÔÓÕÓ˪’˜›œ›š™˜—‘–—•‹„ƒ„“¬ÄÑÙÛÚÌÄ·¿¼½¶ÀÉÎÔÖÖÑÒÓÑÐÐÍÉÊÌÎÌÈÅÉÇËÌÌËÎÒÐÐÓÖØÚÛÜÞàáÜߺ?(1-*+,32-21364774882/,+*--05<1675543($$'4Sk~Œ•››˜›—‘–—Ÿ ¤ŸŸ¤žžžœ Ÿš›š™š›žœšž›š—›™›˜™šeebccccfbdbccfccdja`cc__ecot}„ŒŠ‘”˜¢¤¥§¥¨¦¨«¬©§©§§¤¤¨Ÿœ˜’Ž…{woeYRIJNQMPWXVV^^]ahfebahehffchgdcjgcac[hellkhjmmlnpotplof`]k¶ã×ϼ²vlwtgc^[ee^hllhmyqdcpvodbivzvvrv{{ywy|~‚„‹†|sqxzywtlgksŠš£ž›¡¤”ŒŒ–¥ªžˆ™¥©¯¯±³«®¬±³¯²±²»¶²§ªµ·³¯©©¬·¸±±¿¼¶®«¥•ž µÀ¿¾¸¸´°³²½ÁÁ½»¶¹ººÁÄ¿¼»½Ã½º¿ÆËÊÈÈÇÇÉÉÊõ–•§¶¾Ã¾½¿¾ÄÆÄÊËÈÆËÉÈËÍÈËÉÊÇÇÉÆÇÄÄÀÃÆÇÉÈËÎÏÐÏÐÒÒ¾–•š›š•”˜’•˜“—”މ…‹›µÁÍØÛÓÐÆ¸»ÁÀ±»ÃÈÐÕÖÓÓÒÑÐÐÐËÈÉÍÏËÊÈÆÈËÌÌÈÌÎÒÔÓÖØØØØÝÝààÞÞ®5*/+1.453,/2.6538630.00)/.*/531567973(#%+<_t†——œ™—–—‘”—𡤠¤Ÿ¡¡ œž ›œœ›œœœœœ˜›œœš›œœš—›ffi`ba_ijhbccefcc`cada[cchouz„Ž‹•–™ž £¢¦¤§¨©¬¨©¨¥©¨§¢ œ›“ŠˆyvqgYOPJIORSWXT]^[cfdilfhfdgeffja_jkeh_e`fedhkgmohqopmpnlihb`i¯ß×ɺž‚Šy{wnje\cffjlhlpki``nnf_fnswuutyxwxu{{{|~}ypr|‚}utmllr†•™—£©¢™ž››—‘Ž‘Šˆ’¯®‰‡‘¨ª­±­¬­­±³±¸¹¬¦¨­´¯ª¨­¶»±­¯°¶¸¬²Á¾®¡™£«­²º¿¾··º´³¹¸·¾¾À»»»¹ºÁ¿½¿º¿ÁÁ¹ÀÆÉÆËÌÈÊÆÈÇ¶š¤³ºÁÿ¿ÁÁÃÆÇÄÈÆÉÇÇÅÊÌÇÈÉÈÆÊÇÇÆÅÆÄÁÅÅÄÉÅÈËÍÏÏÍÖ˨’—™™œ”›–”™’Ž‹š¸ÃÎÑÙØÐǹµ½Ã·®ºÇÇÑÓÔÕÒÒÑÐÑÍÉÉÊÌËËÈÇÈÉËÌÌËÌÏÏÒÕÔ×רÙÚÝÜâÞßÙ”+'*)/6/:<101/4103056/'**-*,-32/84/4(+-''.@QoŒ—›œ›—“–—™œ¡£¡ŸŸ £¡ŸžžŸžž ž˜ž Ÿžœ™›œ™™™š››™œcchbgdfdfbabadbahabcccaccinw}‚Œ‹•”–˜˜ ¨¤¦§¦ª«¨¬¨ªª¨ª©«¡™›’Œƒ|spcZMPMORNVVYY][^]a`deffageeghcabgeid`gbhhjbgflokjmumlihfc^`pÁÞÒÀ²˜‹~„„vtpbii`blgioifbeig_Zevwsxuxzvpmw€|~ƒ|zvomr|~uyqqijt‚ˆ“—šª¤›‘””–’”œœ™Œ†‘˜›£¬²­«©«³®µ²³º§«©¢®®­¨£­¿¿»²¬®¸¶©²´¢ ¦³·¶±¯¸¼»µ½·²··´¯°¸ÂÁ¼¸³°¶ÀÁ¾½¾¾Ã¾ÁÊÊÊÌÌÊŽ³©¤¬±´»½½ºÃÈÇÅÇÈÆÅÅÆÆÅÃÄÉÈÄÇÈÇÇÅÉÅÈÅÆÂÁÂÁÉÄÃÄÈÍÐËÔι’”˜˜››••–““–­ÁÉÔÜÕÒ˾¸¹Å«³ÁÈÌÒÕÓÒÒÏÏÎÏÌËÈËÊËÉÇÉËÊÉËËÌËÏÒÑÒÕØØØÙÚÜÞÝÞÜßÖ€/*.*+-430110-.0706:312.1-0%-23763104-*-38Kbz‹•–šœ™—“Ž”—šžœ¥£¡Ÿ žœžšœž›Ÿ››ž›œŸœ™›œšŸ—ž˜ššœdd`aihggfdehcdcchf_dbcahinqy„’˜•›ž¥¦¤¥ª©ª±ª«ª©ª¬®©žš–•Œ|vmaYNMPKQQUTU[c[b`\cc]`celeeijef_ghcdbe`dcfdlljikmlnmonkjc^b„ØÛÍÆœ ‡Šxxxndmcbjiejhf[ethZYkotnqszvocfmy€}€€†}tjktzwvsuswjw“Œ—™—œ¢¡“’—›¦£†vzŽ¢¥¤§žœ¦««®±²¹³±¦«°§­¤ž¡­­°®±ÃĽµ­¬²©š¢¯³²¶¸³­¬°¼¾»³¶´µµ±§«µ¼½¶µ­¬³½ÁÁÂÁÂÿÅÊËÊÉŹ£Ÿ°½½¼¿¿¿¿»½ÂÅÂÃÆÇÂÄÃÅÄýÈÇÅÄÆÅÆÈÇÅÇÃÃÆ¾ºÂÅÅÃÂÆËÎÌÑÐÄš—™˜›—”•’ŠŽŸ»ÂÌÕ×ÒÍÁ´¯ÁÈ»´¼ÇÍÐÔÓÔÒÐÐÎÎÍÉÉÊÊÈÊÈÅÉÍÌÌÌËËÌÌÎÑÓÔÔ×ÔÖÚÛÜÞÝßÜÞÏi(*,&*254.8;./22.28.22(.*/,,,15.6.60220/:J`s…ššš—–’”””—¡¢  ¡ ¢ œ£Ÿ žžžŸ›™žœžœ™›ž œ›šœ™•››š™aagc_gafcgf^fagcgb_]`f\dkmox€‡‰•˜™œ£¦§¦¦¬©ª©­«¬¬¬«ª¦¢›š•Ž…€vlc_PJNMMMSVY[cZ\]_d_hbcbgkcedcgcfebecbcfehcjjglmljikkonbaad‘ÙÙÑÄ›••€vjkjglffngc^fgi`\irg`huuph[er{ywwx€|zsmlv€{srtqtr|–—”Ÿ•šŽŽ•“œ«¥‹œ¥«¥Ÿ—š¨°¯µ¶³¸¦¦¤ª¥¥¯  Ÿª¹º´´ÀÀ¼°¨£š™ž²¹¹³°³³±²¨¯¾Á¹´²·³´°²®¬´¿¼³¬¬³¼ÁÄÇÆÅÁ¼ÄÇÃÀ·¦¤¯¾À½ÄŽÀ¿À·¿¾»¿Å½ÂÂÅÄÂÀÃÈľÁ¾ÅÅÄÅÆÅÄÁº¾¾ÄÀ¾¿ÂÆÉÍÑÄ —™—˜–—“›­¿ÇÏØÑÊźº·ÁÁ¯³ÀÇÏÔÒÓÐÑÎÏÍËÌÊÆÈÈÉÈËÉËÌÌÍÌÎÊËÍÍÍÐÒÓÖÔÔ×ÙÚÜÛÝÞÞÝÈS-)033D665450519108,+5***((-/25544716576GSj~“™•–“”•’–›Ÿ¢¡¡¡¡Ÿ¢ Ÿ›žžžœŸŸ™žœ›œœœ›œ™›šœ–œggefcca^c`dafbd_fia_ca`hdprv€…Œ—˜˜› ¤¥¥§¦¨ª¬«®­©ª¬¨©§£¡›”އ}tn`]KPLKIMZ\XZcV`bfbhcbccafhdageihfig`kf_ehchkmjkmmhimgk_Yeg ÚÖÍæž˜†zuhfhlmfnlbdimaZZoqne`rqie`lv{ywtx|tqqgm|z{|xuvpm€”˜•‘š”–›•“Ž“‘˜¢¢†y~—  ¦¢£§¤›› ²¾´²¦¬®¤©¨¥£§¬­¤§¬¾¼°²½¸±Ÿ–”£©³»¸´¬©«°±¯«¯º¿·¯«²³´´±¥§·½¶²°®³Â¿þº¬³´±±«¨°¶»¾»¾¿º·»¶³¶¸·¿Â¾½ÀÅÅÄÅÆ¼¹ÁÂÁ¿¼»ÄÃÁÈÅÂÁ¿µ½ÁÀ½»ºÂÇÈÍɱ•—“˜–•—™¬¿ÄÊÑ×Õ˽³µÇǸ²¸ÅÊÔÕÓÒÐÎÎÌÉÊÆÈÊÈÉÈÈÈÇÊËËËÌÎÍÉÊÎÍÍÐÒÔÓÔÕ×ÜÜàÞÞÞÞÞ·B()0/30393841372/1./+,)'*-62347639=58?@FM_w‡”—š–•“’‘’—™ŸŸ¢¡¡¢¤Ÿ œœŸžœ› ¡œ¢ žŸšš˜œ™—Ÿœš™šš™——cceahbdfaccag_bbfga^e_]ggmmxƒ…‰Ž•š›ž¢¦¤§§©©««­«¬ª¬­¬¬¦£›•…sh`YPQOOLOTVZYaY`]`eg_daadfhgfcfdg_^afaecdhehkojnknjjhjj`_Yj¶ÝÓÊ·°¢‹‡{~{kakpjilkioppcfiiifhnuqlcquzzv~}ykorsx~}zzyom}“ –‘’–—”””Š‘”™Ÿ“”„z’˜›¡¡¡££¨¡¦¬¤²·«©­©ª¨¨§¦¨®´µ¥¢©¾·­®²¬–’ž®²µ±±°ª¯±©­²µ¬°µ»·¯¨±·½³ª©¨¶¼»µ´º¾¾·´¶®œ¡¨­­²´¹¾¾Á¸»°±¬¶¶´·½»ÀÀ¾¼ÀÈÆÆÅÆ¿·¹Àÿ¾½ºÀ½ÄÂÀÀ½µºº¾»¸·ÁÃÄÉʽ š”š™ª»ÅÆÎÖÍÿÀÁÃÊ¿µºÆÅÌÓÔÕÑÎÍÍÍËÅÇÇÇËÉÊÉÇÊÈËËÍÌÎËËÉÍÐÊÎÌÍÓÑÓÕÙÝÝÞàÜÞßÛž05002433356:2-76,00*+)').44,1350078;CCDIXj‘“—•””’’‘“› š¢£¢Ÿžž¢œ£ £ž žŸœžšœŸ  Ÿš›š–˜›œš—™œ™šggfce`adacbacddffkbda_aggntu‹“›ž¤£¤¦¦©¬¬°¬­¬¨­²­®®«¥ ›”‹ˆ~um_`MONPQWU[]`Y[aa^geaeaddf_cffdded``fadfhiehimgmkmhikeba^ZvÏÝÓʹ­œ—Œ‡z€{magglgchooqlmpmcbkkropivwqpsw…zplrpzƒ~~‚}{vqn{’–š˜–žœ”Œ‰–ŒŒ‘¦§‰ty„—›Ÿ›š™¢¤Ÿ«¯©ž—©²®ª§§«© ©£§¶··£ª¼°£Ÿ™˜ž§®°¹³¯¯®°¯¯­®´³²ª«·¸¬©²À·¶®¨®¹À¼³¼¼¿­¦©›œ¬µ²´³¹¾¾¾¹¢²°µ´·½½¼»¿ÃÇþ¿ÁÇÈÄÃÀ¿·¹¼¾¿¾Â»¾Â¿¿½¿¸³¹¾¼¹¼À¾ÀÂÉÆ·“”Ÿ¬ÅÉËÌÐ˼¿»ÆÉž¶¾ÇÈÍÓÕÓÐÏËÎÍÉÇÉÊÊÈÊÊÊÉÊËÉËÌÍÍÍÌËËÌÍÉÈÉÊÒÒÓÓ×ÛÜÜÝÞÞàÖ4-0.,1070;455348222..'(2.44.3:4399:?:FGUes…“—•–’“’—˜™ Ÿ¡¢œ  £œžŸ¢ šœž›Ÿžœ™›šš™›™œ——œ›™ddgaccbgiafaacbageded`_eehry}…‹Ž”œžŸ¦¢¦©©©©ª¯¬­®¬¬´¬¬§©Ÿš”ˆ‚rgc]RRHOPQW[mf^X\_baceabeafbcfcdcgb`cgafjddhfkggkfiojjfaY^aƒÓÙÕÈߦ¦‹„„skncekc`gjhhonngglsiaiqprrnpz{yqkls~y}‚~zpji}ˆ”™“——”œŸ”Ї‰”••›–xˆŽœ ¤£›™˜¥««²ž—“—¦­©¤¦©¯¯©¦§²¸¸­˜¦­š”𫲰­¬®µµ±²´´²µ°ª¯¶°¨«´·±¬±º¹µª®¹Á»·¹¾Â¶¤ ¨´ºº¸¸µ¼¼¶·«£«¶À¿¾¿ÅÀ½¼ÄÈÃÁÀ¾ÂÅ¿¼¹¶¸¸¿¿Á¿¾¹À¼½º½¹³µ¿¿½º·¾¼¾ÆÊé–¬¸ÉÕÓÑÉ¿µ®ÅÇÁ»¹ÄÊÍÎÏÒÑÒÐÍÍÍÍÉÉÈÈÍÉÉËÉÈÈÉËÎÎÍÎÌÌÉÊÌÊÈÈÇÊÎÒÒÔÔØÚÞßÞÞáßÑ,',183768@64,7<963.33-*//32.,-40/1?@AEJYo€Œ•˜™–˜‘Ž’’––›ž¡  ¢¢žŸŸžž žœž›œžžœ˜™˜›™œ›› šš›™šœddgfefdcbdhdhfgcfebcagabemquz…Š––¢¢§¤¦§«©ª®¨®««­«®¬ª¦¤ž‘”‹|spf[RJMRJNTUWbZ[Y[]`degfedgcccbdccb\d_ccfbdiefhjjkggheca[Y\“ÔÖÓÉ·ž¶¥‰ŒŠz€wvgekdciohkkhfgnvveen{rlik|zqom{}y‚‚~~skjuŽ‘•š˜™™“ˆ…„‹˜¢¤ˆtyˆ•”’”™¥¡žž­¯Ÿ –š™ž©«¨¯®´³­¨£­µµ¯˜’𕡍³¸¸°­§¦¯·±¯±²´¯¨§°¶±¨©µ·±««¸¾º·»¿Á½¾Â¶©ª¯±²¼¼½¼¼Á¾¸»·°°·½¿¾¿ÂÆÁ»ÂÇŽ¿¿ÀÁ»¹³³ºº»¼»»¿µ·¾¾¼·»µ²¼Á»¹º¸»¸ÀÆÇ¼¦±ÂÈÔÔÓËÀ³®¾À¹±¿ÉÈÐÎÐÏÎÍÎÌÍËÌËÉÇÈÊÇÆÈÉÊÉËÊÍÍÌÏÏÍÍËÍÊÉÆÆÆËÑÓÒÔÓ×ÚßáÝßàßÐd!%*-1011,535040560,*.+*4,41/3569A9>@EK]k€Ž––˜–•‘’‘‘”˜šž£¡¡ Ÿž  ŸŸ¡Ÿ›œœ›š žš—œš››™œ———šž™˜bbcagbejjhca^]ceab`afbahgmpz‚‹“ššŸ¡£¦©ª­¬¬®­®ª¯«­®¬ª¤ “‘†}unh[TPJGKNOTTT\Z]\b^cbab`ffgaee`bbdbcagfbcdjfhdigjihjefbWYd§ÛÜÑÆ´µ§–ƒ€ztjodbjniokle`clnkpmruttsps{xuolx}{…~}ysmjw‰™‘”•—•–’ˆ†ŒŽ’›š{{~“–œ•‘ž££§¡¨§ š¥§¡œ¡™ž«°±·²²¶ªŸ °®¯œ‘Ÿ¢¬³®±²¶¯¥¦¦®¹·³²´¬­°­°¯®¤©¹·²«­½ÂÁÁ¿ÅÀ¹±¬°¯´³®±³´¶¾À¾À½À¹µº½¼À¿ÀÂÄ»·ÄÁÀ½»¶¹¼¼º´µ·¹º¼¾º½º±º¾½¶¹¸°³¿½·¸»¾º½Âƾ¿ÇÍÏÑÉÆ¹¼¾Ä¹¾ÃÅÊÏÏÒÍÌÍÍÎÍËÉÉÊËÊÈËÃÅÅÈÈÇÉÌÎÍÎÎÌÉÌËÐÇÃÂÃÄÎÔÒÔÓÓÕÝÞÞÜàáÞÃK%$,,)(2-',027G7?8/23)()40.,-1728B?ENIXlxƒ–™™•“‘“˜›œ¡ ¢£¢¡¢£Ÿ¢œ Ÿžœ›žž›žžž›™œ›—ž˜šš™ddaedecgh`eaaae_leccb_egintvƒˆ•Ÿ›ž¡ª¦¬««¬ª°¯³®­¯¯«¨«£Ÿš”‘ˆ~uqbXRLMPLSOWX[Z]_aaY^ecgdggaaeadbd__deg_^fbaacjflsgkgea^^Zl¾Ü×ÑÀµ·žŸ”Œ‰wpmncfjsrmhnbdhhlpiprsuu{ytrwsp{wz}„{ztkjfvŒ•š”—™–—Ž“ž…ywv‹““—™™•›ª´ª™™“¡ ¢¡ž¢¨¡¢¯¸¹¸´¼·¡ £¦Ÿ•™¬·±¶±©¬±³°­§¦¥¶¹±­¯¬³²¬©©¨¦°¸¶°®²ÂÆÅº¹±¬±¶·¸¸µ¶¶¶³±¹Á¼À¼À»»¹¸»»ÁÁ¾Á»»ºÁÀÀº°²¶ºµ·¸¹·¹ºº¶¼»´µ»º±²´­®±··´¯»¹¹ºÁÇÅÊÎÍǸ¼ÇÃÀÁÈÇÈËÐÒÏÍËÈÌÌÊËÊÊÇÆÉÈÆÇÄÅÉÊÉÊÉÉÎËÌÏËÊÍÌÉÃÂÀÇÈÏÑÑÑÑÒÔÛÝÞßàßÛ©4(,%+0*,361,,29216/5.'.),74<3/;;=BH\hvˆ‘™˜—š–‘““–™žžŸ¡£¡¦¢¢ ž žžœŸ œœœ›œŸž›œžœ›››œšœ˜žœœjjd`ecbdc^V_b_h_eadffgcejkpvz€ŠŽ‘›™££¥¨«¯««¬¯²¯­­¬­ª«¨¦ œ”…{rs`[SRJGKSOTWZY`b[]^c_^bebd_ddba`ecdcab`j_hbfdkmkhieega[Z`vÎâÓÌǬµ¼š“›~}…|n{ƒtknibhrccknjulqsvqqls|z…wyz~€yxmfs”‘—›ˆŠ…‹ž˜Š{x{‰’Ž””“—œž’’££š–”–œ£ §¤©°³ªž¥«®³­ª¡•¥¥§¤²¼µ¶ª©¥¨²°³´«¨¤­³±®¯²¯®¥¬¨¦¤¨´¸»³®­°·¸¶µµµ¶³µ·¼½²°²²³¹½½¿Å»·¸½¿¿¾½¿º¶±±·¼·¬§°²°º»¼¹¹´®¸·¶°¸¸±©©¤¥¨±³³ºÄÂÍÍÊÄ´ª«»ÇÉÃÂÃÃÊÏÒÓÓÒÏÌËÅÂÅÉÉÉÈÈÈÈÈÇÇÇÇÃÇÊÊËÉÏÌÉÍÊÍÎÍļ·¼¾ÅËÌÏÓÑÑÔÔÞÜÛÝÙÙËi)(&$2+..4/.+454/.,.*0'>10178>73:.6:PNiuˆ”›—˜”’–œŸ ž¤¢  ž  Ÿ  Ÿž›Ÿœ›žœ¡œšžœž›Ÿš™œœœœ™›™šjjg`_b`e`ccdaabaaaaac`cekfqr|~ˆ“››¡¡©¬­¬¬®®°°­¬¬«°­¬¨§Ÿš–‡uocYQJGMKNLW[[VZ_bc]ffd_^dd_ed_bcdebc__h`feaeelpkgjefka]X^{ÕÛÕÍ»³Ã¸¡”“‚Šƒxw„‚vslpokmghnkkhos|nhcjrzyttzz€xyrhp’”””“™–†ˆ¨ˆz…ˆ“œ–”“˜˜¨š€…— š“’™«¨¬­¬°­³§¥ž ¨« –˜¥³²ª£¥±³¯ª©©«¬°±³³©©§£´¹®°¬®°±©¦ª¬©ªº´­¬®±¹À¼¹´´¯±±¹º¯©²·µ¸º·¼Á¾º»¶º¿Á»¹¸µ´±¬²´²ªª­²¸¸¹º¸¼®«µ³¬²³¬¥¢§££«»ÀÇÑÌþ·®¦©¹ÃÆÈÁÅÆÌÑÒÔÔÐÏÎÍȽ¿ÆÇÈÈÇÈÈÊÈÇÄÆÆÄÉÌÉÈÉÎÍËËÍÌÐɺ·°´¹ÃÇÊÎÏÒÑÓÔØÚÛÙØÚ¶B)% &,/0)+1.2-13716-').-,,4:756+4(/28++%0@Wq”š™’’‘“—›Ÿ£¡   ¢¡¤Ÿ¢£¡Ÿ¡Ÿ¢Ÿž šžžŸŸ›¡ž Ÿœ›žžœœŸž¢¡šcc_`cc_g_mjcaa`bc`e`[aaabehs~€Ž‘— ¤¥¦¦­­­ª±­ª­­­¯«­§¢¤—•‰‚xtcYPLNJLONSW[[c`ba_`cddaee`cbcgcac_d`facdbdgiechfkeehc_[`‘ßÚÎȱ³±ŒžŒ…‹ˆsy“…{umjgdhhghxqjknrqjrw}swqljqohy„ˆ”‘ˆŽ˜•‘”‚ˆ”˜’‹uluˆŒ”™˜””“Ÿ™™™•Šƒ‹’Œ”¢­¦› ³´µ¨¢§£™‹ž¬²µ³¯²²¬©« ™£¬µ´®ª®±²³¸´­§¯ª¯²­°«©¬¢œ¦¯¯­¦®´¹µµ²®´§¤±´·¸»¹¶³­¬¶º¼¾À½À¾¼µµ¬°²··´¹º¸³¯¨®¸­£²¸¶´¹µ¬¤¯§—š¡¡´¿ÍÎ͹µ«ª¹¹ÄÅÅÇÄÉÌÑÐÍÎÏÌÌËÍÈÆÄÃÃÇÉÈÈÈÉÈÈÈÇÈÉÈÇÊËÌËÉÍÍÉÌËÏÒÆ¶¦¤¥§¥ª³»ÁÍÑËÈÊÑÓÛ×ÔØº>-**&**./-0+4421:A:2&+/7,2357=841*)6Hay†”Ÿ˜“”“—™œž¡  œ ¢ ž¢¡Ÿ Ÿ ¥¡ Ÿ¢  Ÿžœž¡¢œŸŸžžšŸ  œš__ccgeac\a]`aa\fe_gc]_^fdfquzƒŒ‘™›Ÿ£¦©««ª««¯¬®««ª­­ª¦¤£›“ˆ~ysa\VLLMPSVY\X]^^[^`aecdbehe`_afecca`cc]ccagigfihkkgjd`_W_™àÛÒʲÀ¹ ›¨¢…ƒ”‘‹z…€‚zijsdelihllfltulfq€ƒ€se`efnxŠ”ŽˆŽŽ“˜ŠˆŒ†“œžymqv‚‘•’”’›š›—œœ”‘Ї‡—“™ «œ˜Ÿ­±¯žž•“”œ¦³¯±·µ°¯«¥¦¨¤ž£³³°¯§¬²­¶µ³¬¦¤¥›£¬­±²¨ ž¡¬­­±®³·¶§¢ª°°±°²·¼»¸¹¶«²»¼¿¼¼»»¸³°®°­°´´³µ¶¼·¬¥±¸§¢·´²³®©›£¡˜›²ÂÊÏÐľµ«¯¼¾ººÃÄÅÈËÐÎÏÏÊÌÌËÊÌÇÆÅÄÅÃÇÆÈÆÈÈÈÈÂÆÉÇÉËÈÊÎÌÊËÊÎÎÊÏʸ®¢žœ¥¡¨³ºÅÍÆÀÀÅÍÔÕÒÕÓ•*.-%)/,,-/1910,/61.53+..2-10215410->Rm€•œ˜˜•‘‘Œ”–Ÿ ¢  Ÿ¤¡£¢¤ŸŸ›¡Ÿ¢¢¡›£Ÿž›œ œŸžž›ž¢¡Ÿœžœ¢™__fech^b_adbcbbee_ibbb_aehru{~‰‘˜£ ¦§§ª¯­¯¬¬°¬«¯¬²¬©«¢˜•Š}vrd[VSROQTU_\[]^^_[d]ffgefdbf_`ebegaagecebbhhhgejiicecb^Z^”ÚÛÒ·½´ª·¥—“ŽŒ–Ž~€vipninohdcceutslow~slfbedqˆ–—’„ˆ“‘‰“Ž…•’’Žsn|„‡‘”—–›—›™˜˜ŠŠˆ—˜œ–‰‘§¤œ—©¬Ž•ž¨ª¦°ª«­¬±¦¦¬¬¥§§Ÿ¡§´¶±®­¬«°²±ª  •™›¦²³°¦¥¥¦¦±³°¤ª«££¨®¶¸²±±»¼½º»±¯°¸»¼º¸¸²´¶°°³³³·³²¶¿·º«¥¹««²²®©¦˜ ¥ºÉÐÎÉ¿´²¯¼ÃÃÀÂÃÃÆÉÍÌÏÌÌËÊÉÈÈÊÅÇÅÆÅÆÇÇÉÉËÈÇÈÅÉÈÈÊÈÉÉÊÍÍÍÌÍÏËÌͽ­© ž¡ŸŸ¦¯¼É­µ»¼ÊÑÒÐÕÅ](&)*4*1-,4./61-02/1++-1//.1+2138:2>Idv‚‘˜š›–’”““–œœ  ¡Ÿ£¤¢¥¡¢ ¢¡Ÿžž¡¢Ÿ¢Ÿœ›œ ™ŸŸž››ŸššœžŸœž› œœš ›cc`aaaadd_cj`a`ba^f_^cbbkhmtƒ‰”–ž¢¥¡¬¬¬¬«­°¯­¯­¬­­®¦£žœ–މ‚tlbUXNQLNQXZY`_abb_bdbchef`hga`c`fccc`cdaeccebjcagghdcbYZY…ÚÙ̵Ķ¥²®©“°«‰…šœ‡„ŠzuortnqnaSUioshgmw~xvrijfgr†Ž•’ŽŠ†ˆŽ‘‡ž†qsvu”–——’”‘š¡—‘•”ŒŒššœ¨£‹‰§¦™•–™‘¢­²¯¯­«¨¦¥§¤¬©©¨¦§£Ÿž¤¨µ´®ª¨¢©¨¢¦££¤¡œ¥®¬¨¯ª©¬¢­¦›  ª²¨ª®µ³²±³»½»¼µ·®³µ¸¸´±±°´°®©¯·º¶¹»¶·´²ž°± ž²«¦¢œ¢µÃÍÑп¶·°¹Ã»ÅÃÃÈÊÉÎÉÌÌÉËÇÈÃÆÂÆÃÃÄÆÅÆÆÇÊÉÈÈÆÇÆÇËÊÊËÇÊËÍÌÐÎÐËÊÏÇ´§¦žŸ›› ¥´¿»Ÿ«´ÀÉÍÏÓÔ¢>(-*(<2767.-51755:/-,*&+2-.-+03:;IBEXr’—˜™™’•’‘•–œŸ™¤¡£ ¢¡¤žŸž¡ž¡ ¡¦  ››ž£Ÿ›ž¡¡œ™žœž  ¡žšœš›ž›``c`c]`e\a\\__bb]cbb^b_aghov|…Œ”˜šœ¢¥ª­«­­±ª¬­­®¨¨«¬¬¨¦›–ކ€vn_WQPNMMRUYX`\`ac[abdbdkded_`fdccdaace`cgebefhgghfcgfdXZ]˜ÞØÇƸ¬¬± –©™‚Ž”Œ‚ˆ~qzvtobWS]rmlcjxywxuxng_s€‘Œ|…‰‡ˆŽ“¡™rinxy˜””–˜˜–””•——„••‘˜™¡Ÿ›˜‘‹…Ž §¤—‡ˆ•£ «³°²¯¨¤¥£¤¦«®©§«©©¬¤žžª«²­§žž¥©ª§¢§£›œ©­¯¯®¥›ž¢¡ª®®³«¬«¨±³­­¶º¸»¼·¶¬®´±³°°¯°°¬¤§­¹µ´²³³¯´Ÿš®¤—©¢¨ž¥­´ÉÏÊĽ²¨µÁÃÁ¾ÀÂÁÍÏÏÌËÇÊÉÈÇÄ»¿ÁÀ¿ÃÃÇÆÄÂÅÈÆÆÈÈÉÄÆÊËÊÌÈÌÍËËÏÎÏÈËл¯  š›ž¦°¾º“Œ”©³¿ÄÈÏÕÐs-,'+,1@9//7/25>202+1))//40)()-4CLQOTiy‰‘–—˜’“’–˜œžŸž¡Ÿ¡¢£¡¢  Ÿ¡ž›¢¢ œžŸŸžšŸŸžžŸžŸ¡ŸžŸœœŸ šœ^^b^\`ega]_b_a^e`c\^\bfcggix~„ˆ‘˜œž¢¨ªª¬©««©¬±¬©ª«­®§§£œ”‘ˆ„vkfZQUKOMTRYXb_^`_]bdcea_`add`cd`ebfacabcjfbbdgfjdhhbf^`\^šÝÚÊìõŸ‘©Ÿ—‰Ž‘Œ†Š„ˆ~}ypi[Xaia_gfs|wtouse`x†Œ“Ž‹€€ˆ‹…˜Ÿ“uooq‚‹—–’–œ—–Ž”‚‹•ž˜ –•‹ŽŒŽ—Ÿ¥•Š‹ž¦©¥«²²®««¢¤¥¤¤¦«ª¨©­¦ª¤¡Ÿ¦¬¯¬£Ÿ¢¥§®¨ ¥«©¢©«¨©Ž“𡣬¬­°«®¬­°°²©°µº¼¹¹¸®°®±±²³¯®°®¬£¤¯±µºµ¯®­§•¥£” ¡ª¸ÁÆÏÁ¹³¶¶º¾»ÁÃÄÆÉÍÒÎÍÆÉÅÅËÇÅÁ¾ºÀ¾ÃÂÃÄÁÂÈÈÇÆÅÉÈÄÆÈÉÊÉÇÉÎÌÉÏÎÏÍÉÑÆ©¥›–˜¡©±½¸Š•¦¶»ÅÄÎÖ¹?.3-#'<8E-3/2/710.1(3))017-,2/3;@FOOgt†•—•˜”“”˜ŸšŸ ¡¡¡Ÿ£¢ ™™Ÿ¢¡ Ÿ ž¢ Ÿžž—ŸžžŸœœžœŸŸ¤œ  œž›œ ›eece`a^jZ]c_d`a^^`d``eh`cekv~…“œž£¥§ª®««ª­«­ª¬­¦­§§£¥¢š“’…‚umaYYNQOOLPYT]_aaeahiidf_^edeebceccdabeaddfdghcilc_edc^ZT\—ßÕÆ³Àİ™¥§Ÿ˜¢¡’ˆšŠ‰‚†Ž†{xhfdem`WPbqupnqpqhkt‰‰†‡ŠŠŠ‹Š…‡Œ™‘nry}‡Œ‹—”—šš“ŽŒ‹”‘Ž— ¡–‚‰•˜•™–‘’™›“¦ª¬ªª©­©§«¤¢¡¤¬¬ª¨²«ª« šž¢§¯´¢ Ÿ£§¨§§¨­ª¡ ¤˜”œš¢¦¦¥ª©²±°±­°¦®«¦­º»»´³²¯ª¨±±¯­¯¯§¬¦§¢¬¸¹·³­¦«”–¦—¨µÀÈÈż²¨¯¿¿ÃÀ¿ÃÃÄÍÏÏÌÊËÇÇÅÃÅÃÁ¾¿Â½¾ÂÀÅÄÅÅÈÉÈÇÉËÌÉÆÇËÉÉÉÉÏÎÊÏÏÐÊÍѳ£ ›˜ž¨¬²¼¾“~ƒ›¨¸ºÁÆÑÓ’/&&/'')/=42J;512,.++*-2/27+,1.:=LUV`q}‘•™•˜”‘—›Ÿ Ÿ  ¡ ¢¡ž¡›ŸŸŸ  ¡Ÿ¢ ž¡›¢› Ÿ˜¡žŸ œž›žŸœžœ™žžŸaa]db]a\\_a\[]a`ad_]beafbhpw…Ž•™›Ÿ ¤¨ª«¬¨¨«©©ª©¬§ª¬©¤¤™—‡uldXWKLNNLUXZ^_\[d`_bm`edcc_bcdbcaeac^badcddhacdjiad`d_XV`¦ßϺ¼¾¸´®¤²µ§‰—ˆƒŽŽ~xpkhdia]T`juqjoqj_dwŒ‘ކ†…‹‰ƒ†‰†…’›nvvqz‡ˆ‰ŠŽ“™›—’”’†ƒ’–˜–›‘œŠ’¡œ ›™—ކ‰Ÿ¬£•—¢§«¦¡¦¤§©®¦§¦¡¨©©¨¥¬­¥£ ››œ¡¸°§ž ¥¦©¬¬­¡Œ˜ž §¥Ÿ¥¬¥¤¬¨³°©³± ¦³¦£±¹¹º´­°¯¥¦¨¯«¯°®¤ª®¨ ¯¶±­¬¡¢œ™ªºÂÏɹ¶­°´»µµ¿ÅÇÇÎÌÌÎËËÈÉÌÇŽº¾ÃÁÅ¿¾¾ÁÁÆÆÅÅÇÉÉÉÉÈÌÈÊÈÉÎÉÈÊÍÑÏÎÌÐÎÍÑÇ¤šž˜š¤ª³º¾˜||‹–¡®·ºÇÍØÈY%%(%&))*400I@;5-,*')3-263/,/338BJP[l|ˆ—™—’–’”Ž”šŸ¢Ÿ£Ÿ¤¡ ¡ŸŸ¡¡œ¢ žœŸŸ¡œŸ Ÿžœ›šœ› ž›Ÿ¡›  œ˜›œœbbb^[[`]]^X`\^aZX_b^\_bbdepyˆŠ’•–ž¢¢¬©¨ª«ª««®­¨ªª«¨©¦¤ š•’†~wjg[YMKMRNQRW]Z^_d_c`daafbdbb_j`]beccb`b``ffghffhf`da`]UXe«ÛÊÃĹ©­°­–’­¬’’zŒš‡}vmclgd[]_qhhkjlk\ZuŒ“’ŽŠŒ‡‹‰ˆŽŒŒzcw~{†”Œ‹Š’›™’†‡“Š‡Žœ™z˜””—¢¢œ•‘…‡“¢©®§ššŸ¦¡œš¥®ª­±¢¢Ÿª¬¥¥¦¦¤¡¡¢Ÿ–›­®²ª¡££¥©¦›ŽŸ¨ªª¬¨©¯®©§¦¨³­´¯¤Ÿ°¬¦©®¹¸µ³¯­ª§§¨¬°­¬ £®«§ªµ­«¦¡˜¡”¨·ÃÇÊdz¤±´¾¿º³ÀÃÄÍÒÒÎÌËÈÈÈÇÆÃº·½ÁÂÃÿüÂÄÄÄÅÅÈÉÉÈËÇÈÈÉÉÉÌÈÈËÎÑÎÏÍÐÌÐб˜“𢥤¯¸º–}€„¢©¯²¸ÅÒÛ¹=&*&)&,)+418G840-.&++./7515-,,5?@FTaqƒ–•™œ”—•““•Ÿž Ÿ£¡¡¢Ÿ¥¢ŸšŸ¡žžž¡ Ÿ¡¡¡™žšœœš›œ›¤Ÿ  œ ››¡š ]]dh`Y__``_ab``a`a_``h_`ejsy„‘”›Ÿ¡£¨¨¨©®¬««­©®¬­®ªª¨¤¡–”umb^QINPNMSV]]]`^d`_adhdhdefb^]`bcc_fdf_becfgdekb_ac__ZYVbµâÌÈ¿¤¶¹ª•¨¢¡Ž‡™žƒ—†zikkgbdlknmhslg^an‹“ދЇ|‚ƒŒ”–vjvstv‚‘—Š‘“’•—ƒ†‘—މŒ† Œw’œ™šŸŸœ†ŠŽ‘—«§¨¦ ••˜˜¢š¥¬±´ª¢¢¡¨¢ ž¡¥§ ¤¢›ž¡ª°«¦Ÿ–™–‘§¬ ¡Ÿ¤Ÿ¦©¥§¨«¬¦¥³±©©§¡¨¯ª¬¤®¶°®¯§­©§§¦¶°­¤Ÿ£²¤Ÿ«§§¢˜«¯¾ÇÍ¿°«¢§¸¼¶ºÃÇÅÉÐÑÏÍÍËËÉÇÂÁÀÁÀÀÂÃÃÂÀÀÁ¿ÄÄÄÇÅÇÉÊËÉÉÈÇÆËÉËÈÈÉÌÌÏÏÐÐÎÍÐÁŸ™˜¢¤ª¯µº–~…‰Žš¨®³³¶ÇÖ×›.$$')'&*.2253/603.),10;1/23,.0>DGIZm|Œ•œ—””‘““˜ž¡ Ÿ¢£¡ž ¢¡¤¢ž  ››ž¡ŸžžŸ Ÿž Ÿžžžœ›™œžŸœ›šš Ÿ›``mja_g_\^bd`b][_]b_^[aecksv„Š—¡ £¦§¨®¬©­¬¬­¬¯©­«©©¤ š—“‚vmfZUNNJQTTY]_a]`c`ecad^a_cea^_`_dbab`bfddcihd]b_f`d\ZXUQiÇÝÌ뺹³¢¤©ž——””‘†y†™ƒsjttdalimlnomgmgt†ŠˆŠ‡Š„yyƒŒŽ}mw}v|Š””ŒŒŒ‰‘’Ї“–‰v‰˜£››™™•ŠŽŸ’›˜›£¦¥žš’ž¨ œ²´² ž™žœžœ ¤¤¥«¦«¦Ÿ¡¯¯¥‡‡’™¡¤±®¦£Ÿ¡¨§¡šª©­« ª²­¤¬¡¡§°«¥ ©ªª¨§±­§¢¦¨­¬¦¢™¨£šŸ˜Ÿ¢¢¯ºÁÊÆ·­ª¨±»¸»ºÄÇÈÉÌÎÏÎÉÊÈÅÄÂÀÁ¾¿ÄÂÂÁÂÀÂÀÀÁÇÄÆÈÈÈÇÇËÉÉÇÈËÉÊÌÌÌËÍÎÏÐÐÓÓÐÏ©š›¢©²¹·”…‰Žš™©¬«®®ºÍÛÏn''&+(/)*002/35/0&-&*./5940.-158EQOb{ƒ‘šŸ˜”’‘Ž”—˜›ŸžŸŸž£¢¢žŸŸ  œœœŸœŸžž›¡Ÿ ž¡ž ›œ›ž›ŸŸ››Ÿ™ŸŸ¢aa`ed\\]\`aaa\^\`]`bf^aaanuvˆŒ—šž ¤§©«¯¬«­¬ªª«ªª­ª¬ª¢Ÿ›•‘Їvlf\XPLNQTSUU]]]ce\bcdccdbe_c``fe_`bacggkjcefcbj_ea_^\Y\VtÒÛǶ±½¶ ª´¬’ˆ© ŒŒ’ˆy{Ž|twqxmknfigsnf]t…‘‘ŽŽ‹‡‹‡†~ƒ~“œauƒ‚{ƒŽŽ‘“šŒ…ˆŠ‰‹šŽ‡‘}‚”•”‘šš•Ž‡Œ›Ÿ£ª ›™š¤¡ ž™˜—¡¦¦ž®§¨ —œ žœ  ¥¡¥¨¬««žœ¢¤Š„Ž™ª®§ ¨¬¦¦¢©ª£Ÿœ£®°«¢°°ª§£¦ ª®¨  ¢¡¦«®««©¤¡§¬«¡š˜¤˜•Œ˜¥°ÁÇÅ¿µ¥«¸¸¸¸ÀÄÅÊÌÌÊÍÈÉÇÄÅÅÂÂÀþ¿ÁÂÂÂÃÂÁÆÃÂÃÇÇÈÈÈÍÅÉÉÊÇÊÊÊÍÉÈËÍÍÏÎÐÓÐÒÏÓÀ™™ž¦±¶´š‡…”™£§©§¦­¹ÅÖݶ>'%.&+*0)205.146F*'+.+.365/-,65DGFap€‹–Ÿ™–•”’’•œ›Ÿ¡ž¢¢Ÿ¢§Ÿ žšŸ›œ›šœœž šž¡œŸž ››œ™ Ÿ œœ›Ÿ˜š›œœ^^^bc^]`_]]^Zf^^\Z[`\^adehnuƒ‡™œ¡¢¦¥©¬«©©®­§ª¬¬¬©«ª«©žš“‘‹ylcaXSMMUSU\]X]]^`hbc]c_c^c`f^bdadfdaadegh`bfdbfed`_]]ZTS}ÏÕÁÀ½³±º¹¡›”™’¤“€y”‡ƒ|svtoochhhd`a~‹Š”‘Ž‹‹Œˆ…€„ƒŠ‡Œƒlj{‚x•“•Šˆ–™™…†Š‘Œ”‘‹v€†‘‘”ž–’Ž˜‘ƒ†•  ¢¢¦›˜ššžš£¢––™¨žœŸœ•𣢠§¦¥Ÿ§§«ª©›”‹‘––¡¨¬´§Ÿ¢¥ª­­©©¤Ÿ¥ª±¦¤³®Ÿ¨­¤¢©§¡œ£¡­­¨§¢œœ®¦¤˜ž›Ÿ¬³ÁÊõ­«¯»¼«¶ÃÃÃÈÌÎÈÊÈÇÅÄÅ¿À¿Ä¼¼¿ÂÃÁÃÅÄÃÅÆÅÇÅÈÆÈÊÊÊÍËÌÇÊÉËÌËÌÊÎËÐÑÒÒÑÒÏͤœ——ž¨µ¯œŠ–¤©©ª¨—˜©ÀÑÚ×”/().1,4,10//84/*65=-88.26;/573CD9;102560?Ibr‡–˜™—•”’‘”˜œž› ž Ÿ¡Ÿ œ Ÿœšœžš››› Ÿœžž›œœœžššŸŸœžœœžŸžšœ›__`ab^cbc`e_a^_^bbbbhhllnn{y…Œ—›¡ ¤©««­°®«©¬¯¬¬­ªª¨§£¤ž–—Ї€k][RKILINMQU\]c\b\a[`dbaac`b]_faeccdaahbcb`ba_eb_]__TPOSØÎËÐͽ±Âκœ©¶®™®ž…•˜’’€x‚mc[UdyŠƒŒŠ…‰{z€ƒ‰ƒqhuzx‡“•ŒŠˆ‰‘‰ŠŠ‹Ž“yx€…‡ˆŽ’¡˜’—Œzu‚––™¢¨¥ž ž”™ž‘ˆ™—ˆ“”’—”’{„‹tkaLF©¦£œœŸ¦¡žžž Ÿ£¡¥¤ ¤¡Ÿ ¦£¦¢¡–œ¤Ÿ¡š™¤¨Ÿž¡¢˜¤§‘‹œžš“—ª»ÃÌ¿´±®­­¯¶ÄÆÅ»ÁÉÌÊÊÄ»¾¼»¼¼¾¼¾º½½¾ÄÅÁÃÂÃÂÅÃÆÇÈÆÇÆÇÌËÉËÉËÉÊÊËÌÍËÈËÍÍÌÍÐÒÐÑÒÑÔЫ“š¥¬ŸŽžxneU`lv—°ÆÖßÛª6#()#%-.166<>1.28/2..9IB5922.16?@Xj‘—š–“”’”‘”››ŸŸ¡¡¢ŸŸ› žž›› ™žš™›žžœœ›š›š››šž›œœšœŸœž˜—™œ›™bb`cb`dec`b``^_`bdgcdgkhirt{ƒ…‰”˜ ££¦ª««­«ª¬°­ªªª¬«ª§¥¢œ”“ˆwm]\RQKHHLXVZZ[\`de_a`ffc__`[_^eadaa\^a`eaecdccab_a\WUPNX¢ßÑÕι·ÅÌá°±«•©¦¡‹¤˜‹‹©–’€„†qbZ]sƒ„}…Šˆ‡‹€y|ƒŠŽƒfm‚w„–“Ž‹…‰ŒŒ‰‰‰Œ‹{vy…‹‹‹‘ž Žˆ‰„‰‘—œ™¡œ£ž”ŒŒtmfkinruw€ƒœ™‘€y”“ŒšŠ{kY<}£¢¤¢¡¢¤¤ ž˜“œ  § ¤©ž¢£££§Ÿ˜˜œ˜¤¥–Ž£© š§¡•”¦–Œœ•š²¼ÁÊÅ­žª«­°ÀüÆÊÃÄÃÈÈÅÁ»À¾¾¼¿¿À¾¼ÀÁÂÂÃÁÆÅÅÃÆÄÈÅÅÆÊËËÈÊÉÉÍÍËÌÉÌÍÎËÊÍÎÎÌÐÏÕÒÒÒÓÒÀ”𥬒›Ÿx^^gd_^o€¥»ÖÜÞÒy*+"#%'..6/4,104//0,.0<><525AG8;@Da}†˜˜œ˜”••”™œšŸŸŸ  £ ¡ žœ£ž›™›œš››ŸœŸš› œžœœšœœ›œœž››™˜œ›š›™ff_cbb^``^c^dd_adabdflkimnsz‰‹‘•™ ¡¤¨©ª¨¬­­ª¬¬«ª©­¬©«¦£žš‘‰xnb[UOIIHSMSVXZcYfdbb_`acb`^[]\cbc`d_ac^bbbghddjb``\VTTOUÞÚÕľÊʾ©«À³—•½¬”˜¨©›“¤’Žƒ}gaegx„€„‚†|~‡Žzhiwt‚—”’ˆ‰‹ŠŒŒ‹ƒno~‹Šˆˆ…ŠŽ‘œ}zŽ––’”™œ˜•”‰qc\dljoih[Z][ipvƒ˜™‡rw‘£ŒŽ”–Ž{iPo ¢¡££¢¡£¨¦£žž†„ˆžžŸ¥£¦££—“’••‡bz«¨ Ÿ¦”‹˜œ“Žƒ™¤²ÂÅÀ¸²¬¢°¯¹ÂþÁÈËÇÄÃÇÆÂ¿¼¹»¿¾ÀÁ¿½¿ÁÁÀÃÄÄÇÅÇÆÇÆÉÆÆÈÇÉÍÉÆÌÍÌÌÉÍËÌÌËÊÊËÎÍÎÏÑÔÔÕÓÒÉ£›¤¨›–™|fRPRV]]f‘ž¯ËÚÝÞ»D&!!!#%,,--,07*.5-/-,47<8337454=LUn‚‹™™–’’”“˜–šŸ¢žŸœ ¢Ÿžœ  šŸœ›œ›ž›™››ž›š™šžœœœœ›šŸŸœœœœ•˜›™™˜—cc]bedbc\^ccedeb`ecdkjhhlmu{~ˆŒ’“šŸ¡¡ª®®«¬««¯°­­¬­©®«ª«£›—€xk]\SKEIKQRUTZYZZc`ed_]a__^_a`ef_`b_efdbb_bbbccc_^a]ZTMLP’ÚÙÈÃÒÓů¼Ã»ž¢¥µ ¤ª ‰¢Ÿœ‰£™ˆpcupqyƒ|}x‡}„ƒƒ‘‘chz|w{‚’‘ŽŽ‡‹‰Œ•…s{|zˆ…ˆ–Œˆ…ŠŒ€€—œ™—•”’“–Š}lTdxy~„}umlnaU^^ek‡`s‡•‘ž¢£™Œg]n†€ƒ£¡šž©©¤’ˆŽŽ‹‰‰‰”¨¢žš¨¥–‘xkdS8?{‘››šžŽŠš“Žª·ÁÉÀ­ª©ªª¼ÀÄÅÈÆÉÊÈÆÄÆÇÃÁ¾¾½½¾ÁÁÃÀÄÁýÂÄÃÄÆÇÇÇÈÆÌÉÇÆÉËÉÌÊÍÍÍÍÌÉËÌÍÌËËÎÐÍÏÐÓÔÔÖÒ̯¢§¤™“šŠeWQRQTccdoˆŸ±ÈÕÝÞØ˜)$#"$%$.,/2264,)(//0729:52.191<7I_wƒ’š˜•’’‘–š—žŸ¡Ÿ¢  ¢ŸžŸ ¡ œšœœ™›žš›žœŸŸšŸœœš›˜™š–››žšŸžœ›šœšš™™aaef`bef^cdcc^ccbbecfhjkjput~†‹”•šŸ ¦©¨ª§¬¬¬¬¬ª®­®­­©¨«¤œœ•Œ…|ndZWRHEIINVYY^Zbcgad_aaea`af`da^gb^_bba`ccdae]a\b]_^XQPQ’ÛÑÌÓÒ÷ÆÐ¾¤™²ªŸ˜ ¢™„”ªž‹£œ…{xwyw~x|‚{z…‡Š†€{^jzs“Š‹Œ’‹€‡„Ž‹†~vyŠ{„‡Š‹’™‡yˆ‹—›ž—™”Œ‰v][_x–‡ŠŒ€vuvwnafWNcpdV€‹™§¤¨¤£œŽ€qgca`hp|ˆŠŠ•¢­ œ™•“vlj™ ’𢢕w€~wV58N‚Ÿ— •„Š˜ ­»ÃƱ¦¤°¥¤»ÄÂËÑÌÉÌÊÅÁÀÂÆÄÁÀÂÁ½Á¿ÂÃÀ¾ÃÄÂÃÄÆÄËÉÆÇÇÈÈÉÉÈÈËÊËÉÏÌËËÉÊÍÍËÌËËÌÐÏÐÒÓÔÖ×Ò´£« ”™‹vWOJVSZWalq}›²ÃÑÙÛÝÃ[&'%(%%).-54+/13+--,49177403227=@Ph€’š™™—’”‘“—›Ÿ žŸ¡žŸ¡£ Ÿžœœž  ž™žœœŸœŸ˜œ˜žšœŸ›žš™›ššœ›™—™ž›—ddeacbbc^^`dcc`chcagejjhkrwz‡‹Š•“›Ÿž¥ª«­ª¬®®«­­«°¬«­­©¦£ž¢Œ„yqcZOOEDHOPYZYZZab]eaad`e`gbi`d_cfffdad^bdhcee``_a__\TNMT•ÞÓØÔôÁÓÉ­©°± –«¬Ÿˆ™œ—¨•Ž€„nr{}wq~zx‚€z‚‰Žbllw}€ˆ‘ŠŠ‡‰…†…”pmq~‰ƒ„€‡Š‰Ž¡’…|z‡”–‘”–™šš“ˆx\Yo€ƒ––ŽŠ…‰xxywrrmRJLPN€™£ª¨¦¥¥¥œ€~squrl`[ap†¦© ž£¦§¤†‚hhƒŒwr˜¥™ep…lH.3Y“œ›Š†ž·ÃǶ°ªž¤§§³ÂÁ½ËÑÌÊÆÆÄ¿¿ÀÂÃÂÃÁÃÀ¿¾¾ÁÂÄÃÀÄÄÈÉÇÈËÉÊÈÉÉËÆÍÍËÌÍÎËÊËÌËËÌÊÎÌÌÍÑÒÒÕÔÖÔ»¦¥¥ •z_NKPWWb[_hrzŒ§¾ÑÖÝÛÔ/""&"*''0,7-/72-,4+/1336>>=1789@M^s‹›“•‘˜’‘”™š  ¤¡›¡›œ›Ÿ›ž¢šŸœž›žšœœžžœšž›š››™™››˜ž™™ž™›™œš™š—–™›™—ccbbaabgbca_^aecha^bdddjlpu~„‰•–™Ÿ¢¥ª®­«¬¯­¯±­«­«©¯©¨§¤œ›ƒ{oe[PKDLOOVSRVXY]^babhaa]aef^e^d`fbdcgfbfccdbb`ebd\XXTMOP‹ÙÜÚÈ·ÊÒË´·Â¼¯Ÿ«¬œ‘­—ˆ‡¥£Œ‡…ˆwvqw€vwzoy€}}‰‡†reyvvt“މˆ…ˆ‹ˆ„|‚to~†‚†‡‡‡ŒŠ’ƒyƒŠ–˜–“‘”™™kgw‡‹‰“‹|„~|yximiof\RM9T€˜¬«¦¨§©¤›„z‚ƒwor|v~sidXiy’˜’¬š‹ngh\CQc[@Bp~q`3#/{¥›”•¡²ÄÅ´™¤¢£«¶¾½¹¼ÅÈÆÈÉÆÀÂÁÁÄÃÃÂÁÂÄÂÅÀ½¿ÃÃÃÅÃÆÆÉÆÇÉÊÈÈÉÉËÈÈÎËÉÍÎÎËËËÊËËÊËÊÍÌÎÐÑÖÔÓ̽« –“mVLTYW[Zj`amwˆ›µÇÔÙÚݯB(!''/=*-.1/-/32-++(.766=794/-59JVh{”——“““”•‘—› ¤¡¥Ÿ¡žŸ¡ ¢œ¡ œžš›š›ŸŸœž›ž™œ›››œ›žš˜™™š™™œš˜šš™œ™›˜•™›˜”ccda`acccfda_cbbhdacciejtow}„‰‘•œ¤¤©©¬¬¬ª¬±°¯­­¬®®«¬§¡Ÿ›—Œ„xqc[PMEEOKRORT]Zab__h_]_ccecfd`cfff_`hce`agfdcae]`YZVVNQQ€ÖÝÌÄÍÒÆ»²ÉƵ¢²¹¤Š˜žš€†¥ž”Žœ‘ˆyxps}ytspt{{ˆƒikm{xt‰ˆŒ‰ˆ‡ƒ‹wpu}x†~ƒƒ…†ˆŠ‹Š†€~’‹””•”‘”Œzr~”‹Ž„yqpojlh]^YVY[bK:F~¦¬¤ £¡rƒŒ…†Œ…€„tiTFBER]ohbrgVaa_WX318AF24T^F+"-Y’–œ±»Æ½©š¡› µ¶¾Æ¿ÀÀÅÄÅÈÅÁ¾ÂÂÂÆÅÃÅÃÃÁÃÄ¿ÀÀÀÂÁÄÅÅÆÉÉÈÉÆÊÇÈËÊÈÆÉÊÊÎÏÏÌÈÊÌÉÎÍËÍÍÍÏÑÕÕÏÁ¬£Ÿ”ƒiXYPMT[cW\_^fy…Ÿ³ÁËÕÙÚËi)-+$$*.0/04.-/7.**+*+4363/4-0026Fdw†——𛕕ޒ•˜¢ž£Ÿ ¥Ÿ ¡ žŸž¡Ÿ   Ÿ œš›žšœ›ž›šœ›œœœ˜›˜œ—››œš›–š›šš™™œž›˜ccdbgabab`fl]cdklffighhjrt}ˆ‹‹“™š›Ÿ¦¦­«±­ª®¬°°©®ª««¬§£¦£—’‹„tnbWTKHJJPNSUW\b\[\_^`cc_kehkd`_filccfbccabfaccgcb^XYVRRU}Ü×ÂÓ×ʶÀÊ˼¯·¶³”ž¤“‚w𣉡²•omqgk|utry„†‚r_ft~|€ˆ‹Šˆ…І‰†‹‡†lr{„ƒƒ€€…Š…††‡{Š’‘Š‹‘”š•Š†wsŒ”•‹‰rgdefkbRJE>;?HD2@y””£¦¨¢—„xЇ|……{{una`[TQWu`PEHRRQYCHCP?=F7:E5,+2/./+'&'+*I޹ÉȰ•““ž¦°¹»ÂÈÊÍÇÄÂÇÇÅÂþÀ¿ÂÃÅÅÄÇÀ¿ÀÁÁ½¾ÀÁÅÄÅÄÆÈÉÇÉÊËÉÆÈÊÉÇÈÈÌÎÎÌËÊËÏÎÍÐÎÎÌÎÒÍÆ²Ÿ“l]\XTPWUXVZ_Xbnr€’¯¿ÃËÓÖ×¹P((*$-)0.26.30,17*,02/.94?21*),32Lcv†–•™—””’ŽŒ’šŸŸ¡¡¢ŸŸ¡œŸŸŸŸŸž ž¤ œœš™œž››™š››ššžšœ›™œ›–™šœ›™œ™ššœ˜š™ffgccdfdfbfa`gaedbjimhjjjyw}…Ž”››Ÿ¡¦¬­¬­®®®­´®®®­­±©ª¨£Ÿ—“Š…}of[SJKFKOPUQR]b^[_eb^_bbaecch``ggd``c\d_bcbgbac_]b_][VSLoÃÛÚÑÀ¾Íι±ËÊ¿ªª¬¡€{Ž…{q“¨™…vimjjouq~w~ƒwo]`tzxw†‰…‰…‰‰‰‘Œwr~‚}‡~‚†ˆ‰„ކ{z}‰ŒŽ””†‡‰Œ‡vdx€†“˜Œrh\bdhnebXW_ikaTHXZ`}|•™Ž„‡‹ˆ†oME?JCHE>9Mbz€d7:BZ@A835167,)//+,.'&$#+Rw®ÄÀ®–‘™ž«½ÂÂÃÇÇÇÉÄÃÂÊÅÃÀÁÂÁÃÄÅÅÆÄÁÁÁÁ½À»ÁÄÃÃÂÄÇÇÇÇÈÉÉÇËÈÆÇÉÈÆÇÎÍÒÎÏËÏÑÎÐÏÌÐËÌÇ­•ˆ~le][[[XUWXXZ^Y^Zcu‡š«»¾ÄÏØÚЃ,,(,&).2+/2121--3',6;63559-,,*(17Ln~”•’“‹’–£¡Ÿ  ¡¡ ¢Ÿ Ÿ Ÿ œ¡ žœŸ¢ž›Ÿ›œœ—˜š˜›œšžœ››š›˜š™š™—›œ˜š›˜›˜cc_bblhgedbacc`bgcehkmmrnut}„’”š›Ÿ¡¦«¬®ª«¬ª­­ª­®¯¬­©«¥¦ž–—ކzsfTSQOBKLRTPU_a^]`__d_eb`li`c]ahfjb`acddabeebed_db`_XVRNc°ß×ÄÄÑ̾·ÌÙŰº®£—‹{l‚™“Š|vjenqmrtt{}l\ajwxx‚“Šƒˆ……‡ŠŠŽxw€€‡€‚…ƒ~ˆˆ‹ˆ‰…wt‰“‹’ŒŽŽ‡yjf{†‹ŠŠŠ‰~qX`rvwxdY]quwzmf`h^g…hpu{€‹Œ~hZXNLH<36>9DZVH,,0($%#%)GH(%3c^m‘·¾²˜†•¢¹ÇÇËÊËËÉÇÄÅÄÁ¾ÃÄ¿½ÁÁÀÀÂÂÀÃÂÆÅ¿¾¼ÀÁÁÁÃÄÁÄÆÅÇÈÆÆÊÉÈÅÈÊþÄËÓÍÌÌÍÎÑÐÐÌɹ”ƒnaY[VWV[^]ZX^TX[VcfoqwŸ´³¸ÁÆÍÕÕ´Y-&1.0%*,2066-945.-0+).0:54.++7&-8L^|‹’”–’“’”•¢Ÿ¨£Ÿ   ¢¤¢ŸžŸ£Ÿ¡ž žœŸž Ÿ  œ›™™—š˜™™™››œš™››œ›šœ—™™šš›š›__fddeihkebb`c`beidehlonntw|‰‹“˜¡£¬«§«°®¬«­©­±ª­®¯«©¦£ •ކ~vdUPGHJLQQSWV]]\\c[_acc__a\cbbcgej`cfcabe_ihgccda`[bZWQNY‡ÏÔÖÒ½Ë×˹Èξž§¡‰uupx{t{Šrvxsrwmrsy‚j`qysr{ˆŠ‹Š‡‚ˆˆ…ˆ†Švnqz‚‚„……†Œ„ƒ‚ƒ‡}~ƒŽ‹‘ŒŒŠ‚tatx|†Œ…~wghw…ŠiaikŽŽwxsU@SSJe`OK_t}p\PTHVU00FBHm†mB097:978OD;@Jg~6&"$*$+1>w º¶£Štkœ¹ÅÆÄ¼»¸¶¹ºÀ¿ÃÃÃÄ¿ÁÁ¿Ã¿¿¾¾¿º¾¾ÀÆÀ¾À»À¾¾¿À¿ÃÂÄÅÃÇÇÅÅÊÊÆÇÉËÏËÌËÁ a\][^^b]\Z\ZVR]Z\dkt{ƒ‰– «±·ÂÇÈÇÍÏž4&*+**.3+-.0427/5,-/011',.-328;770.-38Qk€Ž—–”•–‘Ž™› £¤¦££¥£¢Ÿ¤£ œž¡žŸ¢¡ž¡œœ¡ ¢ŸžžŸœœŸš›œš››——ššš™˜››™˜›™˜››™˜—œiijkjgkgigecbcdbbcbmijmmotv€†…Ž“•˜¥§©ªª­ªª¨«­¯°©­«¬®ª£¥žœ•‡na[UPJLPKMTSV\^`\_\Zccedbcb`dcccbmc`a^bfddada\^^`[bc`]TRSRxÕàɺÊÔϾÃÕÓ¹¬¦ƒxƒ{˜•Ž‘‰xŠ—}~sjeghmxs€…ƒ…‚„‰‰‚vmr|{}‹ƒ‚ƒ†„„„|zy„ˆŠŒŒ‰„‚wbc|}„ƒ‹€~}f`ptd_PMNLFGONF@258??B:@DWml[z“mSdaQ7Llf[Cv‰vf3FIOT:,3px9%!$!"$,GWv¨Â¹ž‚qnw›´¿ÃÆÃÀ¹½º¹¼¶º½ÂÂÁÀÁ½¾½¿Äü¶¹»¾ÄÁû½Â¿º¾ÁÂÂÂÂÁÃÇÂÂÄÂÈÄÆÆÉÆÄÆÉËÊź¤}yoY][Z^]\ZZ\XX\[[gvxƒŠ˜¤°´¾ÁÃÃÏÒ´U+$))2/*,,,.660588.+010+/1231;B89;75495?[`Q\^[KK^caO1%-y{(%"! %@Yƒ«Á³š}ts}š¶¼ÁÆÄ½»³°³µ¶´½ÁÀÁÀ¿¿½¾¿ÁÁ¼µ¶¼¼¿¿¼¿À¾ÀººÂÅÁÁ¾ÄÄÄÄÅÅÅÅÇÄÊÆÇÄÆÈÈĹ‹h~…‚~c``^f`VXXW[^afn|ˆ”𠦣¨®²¶¼¾ÁÈкc.*02101.5113544234/.-))*.2:=?>A@M3-/14JizŠ•—•“”‹‹–¥ž¥¦¤¦¡§¥¢¢¢¡¢¦¤£¢ŸŸ¢žœ ž ¢žœ›œžžžžœœš› ›œŸœ™š™šžœ˜›š™˜›–—kkikkpfeffa_fbe_ddegckillp|€…‘–˜œ¤§¨¬©¯ª«®«ªª¬­°¬©¬ª¤£ž™”„zndXYJEHGLUQVeW\]aab^_db^cb^`d`fjfedcb^`fdjda^_aeebfb`_]SKO^²ÔÖÖËÀ¾ÏͺÁ̺–’œ‘‰‚ˆ‚“‘‰¡’‹…„wb_gruowƒ‘…‚‡‚ˆŽƒ‡‚†tq{€~~}€‚…Ž…€y}|u†’ŠƒŠ†ƒ†~}jG‚…‡‰„~€xcaPLW@;7;313554,:J`€…^jf`K1.)3//ZqvB8NZldY=5;!).~G+(#3[Œ²»žŒ~xyŒ®À½¹ÁÀ·´±°·²²»¾»¾À¼½¼½¾À½¿¶º¾½¾º¼¼½¾¾¾¾ÄÁÀ¿ÄÁÁÂÃÃÄÆÆÅÅÅÅÉÌÉļŸnigb^s…„|mc^`gaemv†Ž“Ÿ¦¤¤£¦ªª¬±²±»ÅÊÍ®T1.''*2.2,8.4:>;54:?.*/)./,,-5=CH<3;:2<Qiz^;*%+"#I}@U/#:hް½¡‰}{†•¬ÅŽ¾Â¿½·´³´µµ¼¼¼¼¹¿½¹»¾¼¿¹¾¹¼¼¼¸¹»¿¾¾ÀÁ»½½¿ÀÁÃÂÃÅÁÃÈÉÈÈÆÆÈɾ«„YKblnbxŒ„€nmq{y}Š“›¢ ¦©§¥¢¥££¨«±´¶ÂÎϨR-,'*--,0.-.7/852188;0140+')-0499ShfQ4383*)-6:D6,%&48/3btd?PVC.,,2:Gm†ƒg[JMZi@%&$0J, -4‰H0c²­“yt…‹Ÿ¸¾ÂÆÇžµ·µ¹´°±µ¸¶¹¸¸·¸µ³³¯´µ´µ±´»¸¸··½¼½¿ÀÂÀ¾¼¾ÁÂÄÁÅÅÆÇÇÆÀ \\D.012Ph——”‘‘”‘— ¤¦¥£¥¦¢¥¥£¤¦¦¥¥¡¢£ ¤¤£ž¥¡¡£š¢œžŸ›ž¢žŸœœ Ÿœœœ š›ššœžšœ˜œ›—˜˜šœ˜˜™™”kkkojihhiglcil_feaageejmktr‚†Ž“”›Ÿ£§§«°ª­­¨§¬«««¯­ªª¬ª¥Ÿ™”Žˆ~rm[ZTEFNLRXWXY_W[`]ad\[_^dedbeehdbfbebcgchd`b^hfeadedd^\^TTS[­ÝÞмÆÐÓ¾³ÍÏŪ¦ªŸ¤µ†—«”h]glz~……ˆŽˆˆ……Š‹j`crx}y}z…‡xyzz€|xwv…}upbZNSu€‹Œ‹€oZVX?4GSB@RbUD96AB1-..=EF1+#,49.Gt}zM3LI220.8Joti[^hfX'"$0bR GŽM_Œž´ž‘€v€ª»´ºÄÇÈÁ½´··²®²··±»ºº¸¶µµ±¶²·²°³´¸´¶ºµ¸¼º½ÂÂÀþ¾ÂÃÁÃÇÄÁÁÁ³zA7Pa?3Cb~vdeqyib‚•”¥¦§£¥¦¦¨¤ª¦¥ª¦§¬¯·»ÀÇÍÑ»q.)%'('$+.146/55289=78<791+-*.-).4;89>61+748Xp„”™“’“ޓޑ•žž¥¥£©¤¦¥¤£¢¤¨¦¤¦¢Ÿ¥££¡ ¤¤£ŸžžžžŸžž ¡ŸŸœ›žœœ›œœœœ›šœšœš™—›——™š™—–jjikngjghjjeidbhdifibegjjry€ƒˆŒ“—šœ£¢¨§­­«­«¬­«¬¬«®ª¬¬£¡ š“…|riZ[KFHQKSYSX^\\^`Yb^]e^ea_g_a_fa_e^_c\aefb_dcgdfeeccjc]Y\SOVŠÉàÉÊÓÓŶÈÑ𜤳²Ÿ°®ˆ›œn_fu}ƒŠŒ†‰ˆ…‡ƒxfqxvzz|€xxu~ƒ~|ws|‹ŽˆŽ„xndQKXxy…uYU@:=@OWVSMAA>A87:.0>ON/&#$(.6Hflt];;=7,2418c{qON\zwp\3"&)`wI!\˜r­«–‰zy|Žª¼ÂµÀÅÆÇ¿¼²¶±«°´·º´¸±»»±µ·²´³±´µ²±¬µ²»½»¼¿Àº¿¼»»ÀÂÂÃÂÂÂÁÀ¾¢Z02>Xg]?AW†Ž€tftywdw‹–¥¢¤Ÿ¤¨ª©¨ªªª©¯­·¸¼ÀÄÏÐÂ}-'((%**-/,0020790297245732-,..02118304,3.53Jewˆ‘•’”𡤍¡¤§¦©¥¥¤¡¢ª§¤¤¢¢¤¤¡¢§¤ ¥¤ ž ŸžŸ¢ŸŸ¢¢ž›šœœŸš™ššœœšœ—™™šœ—›š™–˜lljmjikhfehgeddhaiajgceglmt{€‡Œ’”— £¤¦«¬ªª¨°¬¯¬­«­¨««©¨ŸŸœ—ˆ…wiZWMGJJJQYXZ__\_`^b^fb\acaaaadeh__^_]_d`bdeacfidjlkgfae][XSUh¦ÕÓÚÓȾÈÕÆ³¬  ¯¹¬ž¨¢ƒƒgbfo…z{~…ŠŒˆ€|km{‚€}|…}~ƒyvwy{}xsxŠŽŒˆŠŒ‰‚{kkWUPdolP;:B@BEKVIDO>9:<;39=QP7.(*''45_dMmF+0;7E94>exdJ@^x~teC*+,Vƒv"/xž±²‹{wuyޤ½ÈÅÁÂÁÆÄÀ»¸¶²µ´²µ³·º³³·´¹·¶¯²²´¶²¨«±··½½¿¿º»À½»»ÀÂÀÄÃþÀ»—E-#*G_rp[=Gx•yijw~ej€‹šž œ¨«©¬­©©¯±¯±¼¼ÂÃÇͼ‚;&%+9(,/3./306554;565017455.++.8,437:02:3;57,0-.-+4*654232..62Hapƒ‰’‘‘Œ‰’œ¡¢©£¨§§¦¥ª¦¦¤¤¤¤¦¥§¤§¥£¢£ £¢¡ ¡ Ÿ¡œŸœ¡ Ÿ›Ÿž¡žžšžš›š›šžšœ—›šŸ–™››–™jjkjjhihlgfedcdbjddcclfjilrz€‚Š”–𢤍ª«ª¬¬«ª®¯©«¬®­¬®¬©¤Ÿ’’Œ€ukYUNGCGGOPQX]ZZ][^ac_^\cabf_b`dbbe^aacbebb_`cddffmgekff`c[VZT_¦ÜÝÍÕÛμľ«›­³¶´›®šlYXgŒ€†††zz‚‰}tick~„zƒz{€zxzxsyzsxƒ‰†‹Ž“‰‹ˆoV=7J]]GDDP[WTEDMbhQG>E?>-+39]xb-+&$&'(HMn'&M‚}*-o¥ŸŸtt‘¬¿ÁÄÈÆÄÁ¼¿¿¼»¸¸µ·¯µµ´¶¯³·µµµ°®®­®±¯´·±«°¶»¼½½ÀÂÁÁ¿¾¾¾ÄÁµ¶ŸqVMGF927HjmsnRFc‹’‚jtŠzoj|‘™™˜©©©«©¯­±·µ¹ÁÉʹ~;2**5#%'**,-026864446;;;=4830661-,.23307/1388:Tdx…ŽŽ”ކ‘–œ¡¤©¥§¨¦£Ÿ§¨¢£¤£¤¢¨¢¡¦¡¥£¡¤¥¥¢¢¡›£¡ž™¢ žœŸžŸžžžœœŸœžœšš›—›š™”•™™—kknjilqllfhghcedjacdhef_bpsy‹—› ¦¥«­­«­ª¬«©«ª¬¬¬®­©£¢ ›”‹ˆwf\SJADKIXTWWYYYX]_][Z`bb[_ca^]ba_`dh`__`dabadgkiggmklekfd_\WTYÆØØÝÓÆÇн© ‘®¯«¼°§¡t_biˆ‚‹„{|‚|tlou€€{~…y}y|zxx|{|~xƒ‰ˆŒŠ‹ˆxrcN824=CB>+$)-TrkA))((,*/RXl5^E*)&/-7;Idh;1'.d¤‘l" *F’­’q{ywƒ™²¾ÁÄÆÈÃÿÁ½¼½»º¶·µº¶µ´³´µ¶´®°³¯«¬°¶²º¯±°¶¼¾ÀÁ¾ÁÀÁÁ¿¾¹½·¯¦˜qZRHP\V>ESgouf_PtŽŽ{m}}ctŽ›˜–𤧣¨«®ª­¶½ÅÎÍ­f4&*-'% $*0-,-213458@<64EC65A72051,003717:4211@EVl|ˆŽŠ‹‰Œ„„‘𢧍¥¥§£¥££©¦¦¨¤¦¢¤¢ ¤¢¢£¥§£¢¡¢¡¥  ¤Ÿ Ÿž œžž Ÿš› šœ››ž›™™™˜—›–š˜š™›uuknionemikjnhcdabbcdcfecnsw‚~ŠŽ”™ §¥ª«¬ª¨©ª¬­¯±®«¬¬¯©¥¡¡š–މwg\NNKDILOUUS]__d``^\haa`c`baa`_Z^bcjd_badhbaeekdhfgmhhdegb]ZUW_˜ÍáÖËÌÕж¥Ÿ¦¯¨¹¾¦kdm{Œ‰„‚ˆ‚}xunkqzx~„€€zyzyvy~s|‡Šzvnj\fdi[UNG??CUHIFAAaZNHD==>5.*-2=`s6*%%-&+3T`a/d8+//4>PK>?B2..9Wi\*(%6|žŠ9#*Fi•z_nx†š¶ÆÅ¾ÃÇÄÂÀÀ½¾¼¿¾¹¸²·¹µ²²¶·¸¸¹±¬°®ª¨«µµ´¶³µ¯¹»»Á½À¼¿½»½»¶»¹³³¥‘w]LI\h_Q>GifgecK’„oowlgƒ”˜˜“¢§§§¯°¯µ½ÊÐÆ™^4()*)/6)(,-.2400-/24;7>98E520/,5+-),)034722/2@AKXpЉ……†‰†‰“›Ÿ¥¦¤¦¬ª¨©§¥££§§¦§¢¢ž¡¤¥¥¤¢ ¢£¡£¢¡¢¡ŸŸ£ž› œž ›œ›žœž›šœ›š™—š›˜——•š—ggmmnmphrjolijgc`bccedfbfktw€€‹Ž”›Ÿ£¥¨§­©¬­ªª®®¯¬®¯¬­¨¤¡˜•ˆ}ucWWMKKFMPSRTY\Z]`\]^beedg`adcbaa^a^i\a]dcbc`efffkilniffebbc^WTVl­ÝÎÌÙØÊ¿¿µ¨¬©´¸¿°“tg€‚†…‚€„†|}xkkuv‚†‡‰ƒ~ƒ}y}€{yw}|‰ˆŠygihijvukioZXTSTLC<=BVcZI;A//4-,*-A_q…k*""%+**9Qae0]/-+>;:85;35-0'>etN'##A„“m%A[e‡}utag†£¸ÀÇÆÃÂÅÂÀ¿¹»ºº¶²¶µ³±­¶¹¹·¸¶¶¯¬¥§©°´»¶µ²µ±ºº¼¾»º¿¿¸½·±¹ÀÀ¿¼®¨†fQKJXnlRMY`]_aX[‹Ž|ruwhd{Œ›˜˜¡¤¬®³¶»ÃÈοK5+++6)1&,'--./36314.:965@6904*,/9/*,/+5262915CDBRgw†‰ˆƒ†…ƒ—Ÿ§¨¨§¦¥©§¢¥§¤ ¤¦Ÿ££¡ ¤¢££¥¢£¢¥¢¢¡¡  £¢¡¢Ÿ¡  £¡¡ ¡Ÿž ŸŸžž› ›šœ›š››™˜˜•—™—kkjihlmpjjiekchedabba^\cajow€€Š–› ¨§«­®­­«¬¬®©¯±««­ª¦¥¡–”‰|ufVQJJIKOUUXNTW[[]]^`_^cbbcdh__aacfbbb`_^ceadfgljhhkiflnifffc]YSWÂÍÞÚÑÈ»½Ãº°¬·»º©•kxš~||ƒ‡„†ƒ~urnpz{~„€…€‚}~yv~zpsŠŽ‡ˆzwx}w|ywrrdfieY\UA:?EQ[ZO<5/+)&'#0DBYn„i-**$*0.3Wb^1e/).8<902;;6522>nwB-",K€ˆZCp|ŠRcqbo£¾Ä½ÅÆÄÀÁÀÁ¿º¹º¸¸¶¶·±¯±²µ´·°¹µ¯«¦¥©¬°¶·³±¶»³¸½¹¼ºº¼¶°³¶²¿ÂÄÁ¾²¬”x_TLEJpucLSdaYeYp‚{u}lf}ˆ“–™¡©µ´»ÃÅÊ÷ƒ@,,(*+.+*/%+11457:6622:67:?5812-/*)01/,/9;7>239HFDYsyˆˆƒ‚„‚€„‘›¡¨§¨¨¥¥¨££§¢£££¥¢¢£¢¢¢¡¢£¢¤£¤£¤¡£¡¡ž¢¥ £œž£¢Ÿ¡œŸžœ¡››žœœ›š™˜œ›š–œ–’––nnmjljfnhcigifffcedd\c_ccfpt{‡‰”™ž¡§¦§§®­­«­²®­¬¬­ª­§¬£Ÿœ•‘ˆwiYQIFIHMORUWT[[\]ZXecb_caaac_a`dddc`_^`bef_eidihgjlihihhefa^`[TTc“ÒàÑÏɽÁÄȸ¶»¹¯‘i]‡ ‰uu|ƒ……‚€€tvt{|……‹‚{ztztmp‹…ˆƒ~~ƒzuyu^_dcZNK?IFWb^^U>;,-'&)(,GCgrƒN*&/*00)0WKZ;X8'+-<;2+0:5*)(@ppC*%5pŠ}enŽŒ|BWorƒ²Ä¹¿ÃÃÁ¿ÁÀÂÁ¿·¶··µ³³±²²´´²µº²¸±­ª««¬¯µ·¸¹µºµ·¸»ºº·º²µ±¹½½Â¿Ã¿½·­Ÿ„o]\PD]YIAQXS_Z``hQE631.2,*$/9Rxlr6"$(+2\b+;5;:579?BIau‚‡Š†€€w{…Œ›¡©§©¬¦¦©©¢¤£££¤££££¢¢¥¥¦£¦¦ ¡¢£¢¤¢¢ŸŸ   ¡¡  Ÿ   ¡žŸŸœœ£žžššœšž™™š›œ–•›—–™lljllmkmdiigefddbbhc`b__\gioy†Š‹• £¨«ª­ª«©¯¬­ª«®°¯¯¯«ª¢ž˜˜‡€xeYYLKGGFONSRU]V^b]\g`baae`c_cdaa``^badfbadfeijhejknkqponjljhe][XYRtµßÐÈÇÇÂÁ¼ÁÆÁ™mv¥ˆ†zqvpvzrqlnx‡‚‚†„vzyunmovx€ƒyzxunqkedZ\QH>GOKGS[XMYdbdZ@C96:96*'12okTVc‹Žzj—¸­Ÿ¨¼º¹¹´µ±·¸»»¶¹µ´¯¨¬¯°°®³°­­®³¬³®¯´­¯«¯°´²µ´³±¶»¿ÀÀÁÁÄÂÁÄÅÂÄÊËÊÇÅÁº¨™ŒqcXWXP;@gZSUSG@ƒŒ††„‰‹™ž¥‚mR6)+,,'*/,)032320/199492767:7=9:<90,**+/1/225246++2>@MYk~†„‚|}€~ˆ” ¦§©©ª¢¦©¤©£§¥¡§¢¦¡££¨¦¤£¥¢ ¢ ¡ž ££££££¢¡¡£Ÿ Ÿ  ¡ žŸœ¡žž žŸœšŸš˜›œš—–˜——mmmnpkmlehghfififeacba]]__js|‚‡’“šŸ¢¡ª«®­®­«­©¬³¯­¯«­ªª¤ž›š‘‰tiaRMAFKGQUUTUZ]X[Z\c^_\db`b\a`]_bdbceb^dbdbeihfnjllkprpnolllif`]YU[ÕßàÖÉÃÅÄÁÉÁš—Ž}yzzxtxsockux|…‚zwnlkpkpŠŠˆ|€€sx„…|qiVD@@FM?>CIVRcP3?GGFH-+/-//213290,18:=*-,.C`7LK@DldE_{ŒŠv|¦ª›ª´·°³¯¯´¹µ¼º¶µ¯­ª««©ª¬´±©«¨­¬¯´°®°²¯®²®±°­³²º¾ÄÄÀÃÁ¿Á¾ÆÃÅÂÅÇÉÊÆÃÂÁ² zh^SRRE/6~USSZA[„†„‹ƒ—£{rqY9))%))/4-)/,+/1514487:=<47==686894/-'*51052;10627==IQdy†„ˆ…||~‚Œ™¢§©«¨£§§¨¦¦¦¤¢¥¦¦¢£ ¢¢¡¥¤¤£¡¢¢ £¢¤¤¢¢¢ ¡¡œ¡££¤¢¡¡žžŸšŸœœžŸœœ›ž™™šš›–”–˜llkjllfjgikgeedhc``bb\\^]djow‚ŠŒ•šœŸ §¥««­¬®¬­«®¯¯¯¯®«§©Ÿœ”‘†wm[UNFEHIMOPLSX\ZX`b]`c_c]a`_e__]a^bbgebbdcegdhhnlhommtmpjkdkbcd__VZ†ÏÙÜßÐÆÍÆÆÅÀ¤°||u~€xwheips{€}„„~rrljs{‚ˆ‰‡~yy]\|…†ulYOHATG<:5;GH@BX[aZIJE86/0-1-:CEBF@_`BXNC1+*/6.1--../1151/0,(%$+HF<897:8@XaNTPDC=E2/654?EOC4?_f]X_K225..,00/.15173.1)12&*)4PFQilSSY8Lgkuy‰‹”¤«µ·²®«³³¶²¸³°ª©±ª¨¤ª´±¬¬¯¬­³°´±¬²ª°°°¬£¨ªº¿ÅÅÈÈÅÀÃÂÄÂÀÄÅÅÅÃÆÅÆÈÈÅÀ¸«š‡tfb]LJI8)>nZ]TQ6_„„‚†›š“‹~riT82)$(-,)),+0-57;784859<;>886;530.5/.*&*024923200>BCObx„Љƒ…zwvƒŒ’𡦥¨¦¤¥¨¨¥¦¦¤¤¦§¬¢¢¤¤£¢£¤¥¨¥ £¢ ¤ £¢£ ¡¡  ¤ £¢  ŸŸ¡žžŸšžž›œ›š˜ž•™™˜›—jjjlhijikgejjddfdfdea][ZVcint}…”•—ž£«¨©ª¬«©¬ª¬¬¯°­¬¬®§¦¢›˜Œ…‚vm]PLDFKOTJPST]`^_\bafdfbcgffcf````aadb^cdhdegjjghmnttololpkoihd`VVZÓÜÖÔÙÖÇÊÄÉ¿µ›†v{}zzlkoz{y~ywyqju„|‹ŠŽ‚~„€vc[xvtmcdVCOG@>;68>..6=VdDSEDHMRA7>53:GU;(4fkofXJ('(-/4/2.30/-5<3+-/,-*+A^YgxfVV^@\†Œ ž¤¦­±®´±°µ·¸º´·±ª©¬®¤¦©­°¬®±´°³µ¯°©«±°±®«¥¡®»¿¿¾ÁÆÈÉÁÇÃÅÅÃÇÇÇÈÆÅÃÉÊÈÃÀº¯›Œyd^fVA<>,%=iV\NCAr†ƒ„ˆ–•Ÿ™’‚uhJ431*'*)'(/0.38=>98656;;A<9?9D@6406*3#).../7220-86>GZn~ƒ‰„ƒ{v…•™ ¢¦§¨¥©¦¨¥¤¦§¦£¦¢ §¥¤¢¤¡¤¢§¢¦¥¦£¤¡¡£¡¢£ Ÿ ¡ž£¤£ žŸ  š›Ÿ›ž œš—™š—›žš˜•lljgnigjjghifefb`fd`b\[XWeeo}}ŠŒ‘—œŸ£§ªª®¬ªª®©ª¯±­¬±¯®«§ž—‘‡„viZNMECIMPMOSTY]ZXZ\cdbb`de\bbde`gfaecb_cbeibhjjcgnspsqromnkmmlb`[[V€ÓÜÛÒÖÚÎÎÆÂ»£…wqvv}w|wwryv}€zztqtqtx„†‡†„€~|vo\[|qjYWTIJQML=>;20)*89FLHNCAEEUQG>;'6LR8&6q[yx_X2(,+/:.,-00610:/)*,)'),A_l„kXUYaOr§¡ƒµ¯­©±·±°³µ·»¼¼·³¬­­¥£¦«¬¯¬³³¯²´³®¨§ ¯³¬­ª¢¨±¼½ÁÄÃÃÇÉÇÀÄÆÇÄÄÈÆÄÂÅÅÇÇÄÉȽµ°Ÿ„j\bNQ?80(0WjbJOMg‰‰„™“œ——‚v`;.-.&,)&(+,-/-1:8978657<78I;A:41-/3--*,+(/.812.575HIbtw†ˆ‡~z}‹“š ¢¥£¥ª¤¦¤¦¥£¨§©¤¢¥Ÿ¢¢£¡¤¤£¡¥¡¦  £Ÿ¢¢¢¢¤œŸ¡£¢¡ž£¡žž¡£ŸŸ  Ÿ¢œŸ Ÿš˜š—™˜—nnnhljgkeejhgib_db`d`ZY\\dho|~†‹— ª¬«¯¯««¯­¯¯¯¯¯±¯®¬¤ š™ŽŒ„wj^NJGENKPOXUY]`YX``a\`db^facfc_bc`ce]^_^ccfeeimgjlqpsnqqnroijefa`ZY|ÒÙÜ×ÓÛÔÓÍû†rnjtyz|rry|€|~zuhfluz}‰Š‰‰†…‰Šƒ~wjfXOgb]OH967;;88;3:B897224E./(-(*12931-0/:KWm|…ŒŽ†‚|w~Œ—¢Ÿ¡¢¡¥¦¥¤£¥¥¢£¤¦¡¡¢¤¢ž¤ £¦¢¥Ÿ£¡£¢§¥¢ ¥¢£ ¢¡££¢ Ÿžž ¡¡Ÿ Ÿ¦žŸŸžšŸœœ™˜––•–—jjdjfghljimilfabdbc_]\XY\]jru„’–œŸ¡¦¬««¬­­®®²®±«­®¬°«žžž˜‘Œwm^QNHGAOORWW\^\[]ccbddcbbhecjcgbdacedb`c^`cdgfjjptmqpqovpmlikggc]XY|ÏÚÙÚÑ×××̼¥viejv|€}ttytw|{uqggfxˆ†ŠŒˆˆ„}ym^VCJSD313JfYIA.34**0+/32FaK;EDXV[YE)-7HQ8#/;Zv…|oK*+,0-190+))),-(*#$$'6\iozkOO_o‰‚}±—…²·³¶±¶²¶»¶»½»¸´´±ª¨¡¢£¥§¦¦¥«±¶¶°¬«£™¥§©§¯¹½½ÁÄÃÆÀÆËÍËÇÇÃÄÃÃÄÆÅÉÍËÄÅÈÃÁÀº®¨ž„ye_QI=7.0+2N_UKTGpŠ…~’™”˜‡qQ54,.-+/,010/1/335<75E2679;<:4;=1-2.+0+*.06836++1.8Sat…ˆ‹ˆ‚…‚~}‰Ž– ¤¢¢¤¥¦¥§¥£¤¡¤¤¤¥¦¤¡¡ Ÿ¡  ¤¥¥£Ÿ£§¥£¢¢£Ÿ¢£¢ž¡¡Ÿ¡ Ÿ£ žŸœŸ  žœ ž™››™——–—–kklghdmlljlodhfeg_ed`[_[Y^en}‘˜›££¨§§©«¬®­­°°¯­­¯®­«¡¢›™“Š}uk[ROHIKIJXWT_`\^]dcde`cgdgicdbcbhafbdjcdcffihigljonttvvrqqoqhhob^X[ËÙ×ÛÕÏØÚɯŠtebovz|vtxy{z~z}nidqs€Š‡„Š‹„ˆvuv{v]C55=9/+8N_J64+-31)1/237IaYJ>EGRTa;+/6IVB'(.Gvu~b9**-..014+/,.#""$&)7`w}nu\N[w“£‹z±˜“¸µ³¶´¸¶¸º¼»½»¶µ³«¥¥¢ ¡ ¦¤¦«¬³¹´¯¬§™›£ž¥®µ¹º½½ÂÄÄÈÄÈÈÌÊÆÊÇÈÅ¿ÃÈÇÆÌÈÊÇÌÅÄÆ¾·¨ž†th\SSC0020,6MSOHI]‘„‰–™žƒ`G-:5-.612.40,/14436<>>9;?>640+/'32)0.8311(-48:Yjx…Žˆ€|}…Œ•›œ™Ÿ¡œ¢¦£§¨¥§¤¥§¤§£§¤¦¡ ¢ ¢¡ ¤¦¤ ¢¤¤£ ¥¤¦  ¢Ÿ  Ÿ  ¡Ÿ¡ž›žŸž›ž¡ œœ›œœœšš›™”iiolhhjjgjhhigl``ec`aWY[Y\cit‚…‘‘”œž£¦«¬ªª«®®«­­«°®®ª­©¢ž™–‰}pm]RMHJLFMPUO_[a^\d]bcabb`^habaeah_de``dcffbgjmfhlprttssuqrmrmjlb`[[zÀÞØØØÎÒÚЪxcdakqxxoqrwy~{}vnjpw~…‚‹†…„ƒ„w{zx€vR.'+1;4,>?<><+4.*--,,//720.0//5?Qgv…ŽŒ‹†„‹“˜˜š•“—–››£§£¤§¥¥¤¨¦¦¥¥£££¡ ¤¤¡¡¡¢¡¡ ¥¤¥¤¥£¤¢¢¢¡ ¡¢œžžŸ  œž›Ÿ˜ššŸšš–œ–•kkkiknhnfjbefcfeeedbaa^[\djly€ˆŠ˜š¢©©«ª­¬ª¯¯®°®¬ª®¯­§¤ —Ž~ugbVRJCLKMQRPW_e][cd_caec`efehhgf`bd_bacjhmfekfjlonlnnpqtqqqmmnngd^_d¨ÝÝÖÛÚÒÍ®ymggkigipspuvrwnggqz€„…{{|ne„|sns{‚xhW0,&+6?[^6-/,+5***+.0/+6BLTPN;?HVUE8(9hS$#-9+;b‘‡cVQdnbX3%!*"$"(.P†Ÿ£ƒf]c|žµ«¡¤Žz{†°¾º®¯´µÀÁ¿¿Á¿µ®ª¦¢¥¢Ÿœ¡Ÿ¦ª­µ¹¶²°¢Ÿ£§­­­°»»»¼ÀÁÇÃÀÄÄÇÉÌÈÉÇÉÈÌËËÌÍÌÎÏÍÎÍÍÉȾ´«™ˆme\\K4'$01,1PTLAErš™“ž£¨xJ30../4,)+.3-4102317926:B>8>?;47./7-0.(/*326.51:78HXq†ˆ†‚„’–™›–š•”—˜ž™žž¤¡¥¨©¥¥¦¥§£¦¤¡¤£¢Ÿ¡ £¡¡ ¤¢¥££¨¡¡  ¢ ›Ÿ ž žžœ ž ŸœŸŸŸžšš˜ž–™šœ”–ddljkngihg`ggcceffed\c[\[dir{~†‰‘šž¥¨¥¯±­­©«©°±¬¬®¯®«¨¤žž™“‹xfcOHHIFSSRSU[][[\a_a]gbfgdeecikeb_fbcbcdghghhjhlnlnsmtvwvopmpnlj^gbcÎßÛÕÚ×Ê™ljjhjkhijpwurwyvnu|ƒ„„~„kWwzrprryonuiG-'+->W`V,-)/&+/64-+20,138JVOE:?IKE@1EcZ+-<<,2QŽ…X1>@JA>.)&$##!)V‚š ˆrmhxŸ¸µ §¢˜€{Ž »º±´¦·¼Ã¼¼¾¸µ­ª¬¢¤£ ¡žŸ§±´¶¼±°Ÿ˜š£¨¨§¯°´º¹»½¾ÀÄÄÂÂÆÉËÈÉÊÉÊÉËËÌËËÒÑÒÐÏÑÑÎËÁ¹¯¤‰shYYM>.&*./.BIUK>e’š“›­£š}M--/31*+/2-2+3371565:1@:=7@?EG:4/+,,1/3-/2=1*--16?N_vƒ‹ˆ…„‡“—›š›––˜–—›—˜š›Ÿžœ¡Ÿ¢¦§¥¢¤ §¢¥£¤¦¡ Ÿ  ¡£¢¢£¡¢§¡¢¡ £¡Ÿ¡ ŸŸ¡œœœ›¡ŸŸž Ÿ¢ šœœšœ—š›˜”—fffddjchihhbeeiadgdgd`\YZafnvƒ†’—š £§§ª®««­ª¬¬°®¯®­­©¨§žž –‰znjUQJFCOMOTRV[aZa\feda`f_aefagaegccffdfdejejfggkmjjporxwxrqoopmkic_^wºÜÞÖÚÚɉkhpoqnsiiqupmnvqt†‡‚y~€‚tWesqoqofVWPV?/.23Q_aN2+*)'-273/1;315.38STHHEBEAC:@aW+.>@711/513@QLJA=D9AMFYP)+K;5-YˆŠ^')&$.'##!&As¤¬ˆqht‡Ÿ¶¿­¦¤¦¥£†€›¦¤¬¬¢°À¿¶­¬½®§­ªª«¢§ ¨£®±·µž™Ÿ¦¤ª±­¯´»´¹½ÁÄÂÄÌÈÇÆÈÉÊÉËÊÇÉÉÌËËÏÏÑÑÒÐÓÕÕÑÍɸ§–lb\QF1&#&./0?EMF=u–›œ²®œM//216..1CE78?85894567;=AC9?A:503+0/,1222:80(,+46M`u”‡‡|}‹’šœ¢œ™•›™—™˜–““™›—™˜šš œ¡£ŸŸžŸ£¡¢¢¡¡  £Ÿ¨¡¤¥¥¥Ÿž¡¡Ÿ ¢¡ ¡œœœ¡› œžžœ™œ™š™—–™™–cccmhelgjdihigebb_ba`^[[ZYcmv…†Ž”–𠤣¨§«©¬°««­²±®«­¯¯­¨¡›”ŽŠ‡zsaYQMIPQKPQTT_`]`bafhhckebilibhdcffhefdieeiffacgoŒ§{lqurwoqvsnpmgdb`h“ÑàÞÜÖµyrtkqu{xutqmogbi{†Š‚}€zwlchPA@:HWORIKM\\ZC9.7Th\3-)*-&-@C7.A:2+.4+0:@<430-/0010568968,*-16Qiy‰’‘Œ„ƒ|Ž•˜¡¤Ÿž—™—“š™—–™””–›˜˜›˜›Ÿ ž¡žŸ¢¢ ¢Ÿ £¡£ ¡£¡Ÿ¤££ ŸŸ    ¤¡ž¡žœ›ž›ž› š˜š˜˜š˜–“ddfnmifcngjleggcebcbd^b[^`emvˆ’“˜¢¢¥©«©°®­®¬­®®²®­¬¯®¦¢—Š„wleXUHFKMOMTTW[VX^bb_hjceiffkkejkecdi`cedaebcfijmŒ¹™rnmsvsvnsslokkmebg~´äáÞÕ¦turjtuxttrgediŒ…„‚ƒ‡|_L[ZPQB964DZ`d;Bj7,/4P7)-*&$!# !$4k› xkj}µµ¬«¥¥¦¦§£”„§¯¬¢Ÿ±ºÁ¾½¸·±§§§¨ª««ª«­®¬§œ“•¢ ¢©©«®¯®¶º¼»·ÃÄÁÃÄÉÆÄÈËÈÈÈÉÈÊËÌÎÎÒÐÎÏÒÔÐÐÓÔÎÊÄ¿±¥ŠzmbXL71.-*'+*7FR?BŠ˜›¥ª¦†e:3@?62+3<600;7789879;;?D784.*,*04276?70).,-:Qq€‘ŽŠ…ƒƒ‡šŸ¡£ž—œ—›—˜—”•˜—•”–˜•˜–—š™š—Ÿ¢¡ ¡ ¡£¡Ÿ£Ÿ ¢   £££ŸŸž¡œž¡ŸšŸœœ›œžš™œžœš™š—•˜hhkgfhehhfedgcfcicad_^_\Y`iiu‡‹‘–Ÿ¢¢¦ª­ª­­«¯ª®²°®®­­®ª©Ÿœš“Švo`XOIGQHMOUPX]Z]\_cbfhgbjhggicjlchcbehacdcbaffp°£xmosnxttqptpsopnglio›ÜåÝÕ£qmuqrvxx}smcarv‡…z~y…Œ†sjzkg`IGQSC/.3?7.*,,G?23;524176D.+.;>@9227Pcng\z?7-3;€‰G%$(##(#)+b‘¥•tr{ µ¹¥®­¨©¡¨¨ª–‡Ž©¶±¨§¯¹¼Àƹ¸¯§ª«©«­ª«®ª¯¡–“•œ£¦¥©¦®ª±°º½¿¹º¾ÃÂÂÄÆÅÆÅÉÈÉÊÉÉÈÍÌÑÐÑÑÑÐÑÒÑÔÖÔÑÌÃÁµ¤”~oaQM:;2,&'(+07LF,w˜—¡®©†lA3339.)/-15337;9<=9;D9=BB=<843.-//,2.094471**,/E_tЇ…‚ˆˆ“¤¢£žœœœ”–™š–——–—–”•–˜””’–”–š˜–›™œž¢›ž¢Ÿ¡ž  ¤¢ ¡¡¡¢ ž¡ŸœŸ ¡žœ˜žœ›˜œŸ˜™žšž™ž™š˜•jjikhkjlchgficgcdce_``][\bcrw€ˆŽ’”›Ÿ¢¥¨«¬®­­¯±¯¯¬­¯¯«¬­ª œ—‹ƒwnfZSGLNFRNUWY`]b]cgcegdgjjglighibhebbedeeadcelƒ§Žxqrnxyspttqrvutupnpep„¼äßÖ«xnurruyuyytdi~y~zƒ€}}…Ž‹ƒztoohcUYD3.-/2+2&/58jmB'*)/.ALG8,6=0-252B57).6A<8742?arix~P@*56h‰X) %#"$6]‘©”„{w}•·¹ª©¶³®ª¦£°£”‹©µ­¬¯®¹¾Ã¿º¶¬§¬«§¬­¨¨«¥ •“”—¥¦©«¨¬­³·¹¾¿¾¿ºÃÃÅÇÊÇÆÆÆÊÍËËÊÉÊËÑÑÐÒÔÑÒÓÔÔÖÒÏÊÄĺ§™„scRN<40)"#'*36KR1gœœž¬¦‰sJ701908@B+)-8486*+.:H;-983HIgloyYL;;-^˜jA%%.Yƒ¡–…|s{“´¿ªª°¸¹®®©¨«®™Š§¨¦¦µº½¿¿Ä¿³«¥©¬§§ª§¢¥™‘–›œŸ¦¦¨§©¬®¶¸ºÁ¾ÀÀÁÅÃÄÊÈÉÊÄÊËÊËÉÉÍËÐÑÒÑÒÑÑÒÖÔÓÑÏÎÊȽ«™†sgWQF7.&!#*.1KY?85864C-)&'.(+Wn>(+.*8@?/57D4,/37=7121.D<1.1h¤Œo6" )R…—›€yr{ޝÀ·¦­ºµº°²°³³±¡¤¦¥©±·ÀÇÅʾ°¨¦­­«¥¢£¥•’›œ¡ ¢¦ª««ª¨«´¸»½ÀÁÁÁÃÁÃÆÅÈÉËÄÊÌÌÌËËÌÉÑÍÑÑÑÓÒÑÒÓÔÑÑÏËÆ½¯ŸtdUWG;-($#"(&2AZE?“ª¢¤¬cZ98324C97<;2478<99?=>C>BBB7>42-4+1//446600+.38Q`v†Œ‘Šˆƒƒ–œ¥¨¨¨¥ž£œššš›—–—–™—•—˜”‘•“–•–•”—–––“’–—™˜˜œŸž¡ ¡¡¢žžŸž›œžš›œ™š››™›š™––”•˜–”•ffjhjfigfec_ddccddb^ZZ[\jeghu|ƒŠ•™›¤§©«©«®®­°¯°­¯°­±®­¥¡œ•Ž‚pr`[QNORWUSTV[cebcegcecchdeedffghdcdjaeefjgfijmtwqtrrvssuxsvzyuvttttnqijt¤ÙâΗv{vurrjirƒ€}}z…‰zy‡‡ƒ~tkfbd_I.1103@O?+'%,))1OoJ-368>3.-5=G0-3728B117;H=5&$.GiZNd4,&"))(1;TO7„°§©ª|bZA:6:*+-69<1<55B@5@><;B>DC:91-+-12-021581+*'.8Rr‚‘Žˆƒ‚Š‘—ž¥¢¤¢¥£¢¦ž¡ž›š•˜˜•—–˜”“’•”—•–𔕓ޒ‘’—“–‘—•œ¡›Ÿ ›ŸŸœ›œ›œŸœ›š›œš—š›š›—˜š–—˜™“˜kkjlhklkeea`e_ij^beZ_^^]``els}€ˆ‘”—Ÿ¤¨§©«®¯¯¯®¯®¯­°®°«­¦¢–‹„zmaYUNROT[YXZ\]\a\cfaeffgei`edfgjahejdblcejjemppwururmqvvxuxxutxsvwttsnnnŠÆâОwxtotmj{‰w|}†‹|…‚xha^faM9;;01AS\/%')#-(/EiX4GKN5+,/:B111/7746329HK:9&',8k]5.("&(*(2G_5e­¤ªªƒcnN,/2.-349@6;4;9==>86=ED;>666.+-3530778<8.*-4Kezˆ‘’ŽŠ‡…Д𤤤¥¢¥£¥¢¤¤Ÿ žŸœšœ™—˜•˜™™–”š•—–•••““‘““””“”“’•—š›š™›œž›œœ™™™˜˜˜›œ™šš™˜››•™š˜—–•”hhhkhhieefbbfde`a`\]]\^VY]fmtŒ”œ£¨¨«­ª«®±°°±°®­ª®®¨« š˜‘~ulcWQJNNQSX_a\`c_[`bbdfdgebjfjfhjbfcidiegiiffmlv†}moqpnx||uxyvywvxxurrrpq|œÖت€vyrncvƒˆ‡yz…†Š‚wxƒw]Y]VA/6/39S]\M++(++)*+4ZmM?>=4/?AHK1/*28F11,0=R?88/(,8ha.JQLH/'"$.n‘i."5iŠ¢Šxzr{–º«¦¦¬®¬´¿Â¼ºº´¹¯‰˜‡‹©¥¬´¼ÅÉÀ²¬§¢¡Ÿ¡ª£ž˜š›¡ œ£¦©ª«®ª®ª°«²½»¸¿ÁÄÀÃÀÇÈÃÉÈÆÈÌÊËÉËÌÌÏÍÎÎÐÒÒÒÓÔÓÔÔÕÓÑÌüª˜†xkZNI8.*+)+//+;W:P¤©©«]tQ/-41056688<87:6:>;9=;3+Rq]h9'#().)**5Il`;1+/03M\F6-,57:-,.3JT/4;(/.4RV*3MQA+(# +E”{K ,f‡ zw€•±ª§§¯±®°¼Ã»¸»¹¹¬Œ›}ˆ°«±ºÀÅÁ¶¯©¤ œ›©¢”žžŸ©¡¨©©«®®­®®³°¯¹¸¸ºÃþÁ¾ÅÇÇÇÇÈÈÎÍÍÊÊÎÍÍÐÏÑÐÑÒÏÒÖÖÖ×ÖÖÑÎÉ»©™Œwf^YN>1//(()*38UCH—ª©±ž[q>.(14757<75::@8:74<78>@:92183/,.,2:8661*0-/:St…’‘ŽŠ†„‹’œŸ¥§©§¦¡£¤¢¢¢ ¢¡ž ŸœŸŸ›Ÿšššš›˜˜—™˜˜“•–‘’““‘’’•’‘’•–•™——›—š›™›—™™—™™—™™›š›˜——˜˜–•“ddmghgj`ckdfgaka^a\]X_XXX[ao|}…‹˜œ ¤¥ª©«®­¯­°¬±¯¯®®®¨ª¥¢ž•”Œˆuh`XKKLNORW_Z]]_]a^cccelfffhehfiidjhmdgkbiihmkimu|nov|uuzwzvwyywvzwywtovyw}¸—x~yrioƒ‹ƒ„…ˆ…„ŠŠ„n^]C3-1A8(&BPdg3,)&67()+,4JhK6%2.>WN54-<4.:*0*6YK2=N/171.C-4NS?## $3s†Z1Už”}owz·©¢¥¥«¯´³º¾º¹»»º®’šv¬«¸À¾¸ª«¡™œ¢¥š™œ™¡¡ £¢¨ª®­¬®­³®²°°²µ·»½ÃÂÃÂÂÄÅÇËÅÃÍÌÌÉËÍÌÊÎÎÐÓÒÓÑÑ×ÔÖÖÖÖÓÑÊÁ°f`XMD7,-/()$-1T[=~¨¨¯£^n<3.70&0687<5@>CA:;8<=?=39676.31/-55432.'+)2Fau‡Œ”‡‡„Ž”ž¤¦§¤¤¡¡¥¡¤£ ¡ ¡œ¡¡¤££¡žšœœœ˜ššžš››˜—•’’‘’‘•‘˜“’•”‘Ž•”‘’“‘–”—˜”™™Ÿ——›–•—›™™š—šœ———––ggegigbgcffcccd_ccc^Z^^VY[inx€ˆŠ“™—¡Ÿ©«¬¬­®±¯¯¬±°­­­®¨ª¥¡Ÿ™”‚ujdTKIKLKXW\]Yabb\hhbgcgebjgjfjmicefhhfihfhglnintuwnuqrwuz{{uywzzy~{yyuy~‰šŠqszwn|‡†Š‰‡’ˆ€€{|~uaTH2-7KD3'',?nF2*)1E3'0,'*5Ja;.);UXJ9.+/709*,*<_A+E]3-7',/>6Oe?$! ,O}g^r›Ÿ‡toz„«¯£¤¢¦¦ª¶¶½»º¶¿»¸± ˜t}¨¯ºÃÀ¶¯¦¢¡™—›–›šœ™›¡¤¡£¥©ª­ª©¬¯±°³°¶µ·µ½ÁÁÃÂÅÃÃÇÇËÄÆÇÈÉËÊËËÉÎÍÏÒÓÑÓÒÖÕÔÔÔÖÓÓÍIJ£’ƒo]`WM>3/),+''1GbBi¡©®¤iYB3D<2,;63;99::A;A=;==>;97-02643/39763,+)/5E[s}ˆ“‹‹†Š•¢¡¢¨¤¡£¤¢£¥¤¤¤Ÿ¢Ÿž£¦¦¥ž £ ¢Ÿ œžœ˜šœ˜œ™™Œ“Œ’•“•–“Ž“ŽŽ’•“”–˜™—™™—•–ššš›žš™˜›——•—ddfjbicdcggdaec\be]d_^[ZY`dms~ŠŠ“”œ¨¤©­«««¯®­®®²¯°®®­«¢¡›–vj]TUQKLORYWY]``_]`cheeghdjefejhmeeifcfegejmmjlppstwtqpqpslqksonopjefy}…Œ„qnŒqmupz~€„‹ŽŒ†}€€z}l`YN90.EY8,!%&Ev@8*'JT21<*&&)4RH>9ZbS=2&()177*))@f;';Z142%(4F4OzS! # 1bgŽŽ ˆ}jtˆ£²¥ ¤¡œ¢¢¥°´À¼º¹»¸»¹¯¤Š„«°·Â¸®¨¦Ÿœžœ›˜ž ŸŸŸ£ ¢¦¥¬ª®¯«¬¬°¯¯´²³·¶¼½¿ÂÃÅÄÆÆÆËÊÆÇÆÇÊÉÊËÌÌÏÍÏÑÑÐÐÓÔÓÖ×ÔÒÔÎĸª•†uecXP=2+-)()(1GePi¤¬®«nXH1C8,).8>@9:Mcib?67()09;.+,-Rd-&-J<>9)14..cŒa)!(>lŽ—„{qn†§¼­ž£ Ÿ¢¡¦­¯»¼º»¼»»»²®«´¼·§©§¦¡žš›œ ¥¦£ž£¡¦¡¨¨°«°­­°°®³µµ²¶¸¹¾½¿ÁÄÃÆÇÄÇÃÅÇÃÄÅËÈÊÉÊÎËÌÏÏÏÑÒÒÓÖÖÓÑÎÊÇÀ¬›Œte\YQ@4*%%*),17]IX–ª®ªƒYV-9@@B@:81/*+3/2482923-)-3:Sr{„‹Ž…ˆ‡†”™ ££§¦£¡¡¢¡Ÿ Ÿžžž  ¢¢¥¢£¤¡¤§£¤£Ÿžžšš˜š’–’‘“““”•”Ž‘Œ‘’’““•”›˜œš•˜˜–˜—•–”˜ffekfcjifbbjde__a^_g_^[[V`ejyz„“•˜œ¦ª¬¬¬®®¯®°°°±¯±¯®¬«¥£“‰ƒwkeWOKLHOTVTSU^__bhcdcgfcgijijheglidefcglgjkijlnrqtywvzxwvtutvrqqnqwuz‰‹qY[Uay{w`p‡ƒ{€‚‚€|y{xmfWF,+=bZ,'&"%P}R@/@O+$37=/3.07BkheG6;*%"+:6,&)7Yd*%*GQX9@D/(=xqA0# 2Sru~lbjƒž´®¢¡¡ Ÿ¡£¡¦­¼»¾¾µ·¹·´¬œ¢´¹´¬£§¦¤›ššœ¡¦©¤¢££Ÿ¤©§¦¨­°­®­®¯°±´´±²´¸¾À½ÂÃÅÆÅÃÄÁÆÃÆÄÉÈÇÉÌÌÌÈÎÐÏÑÒÒÓÓÔÔÔÐÌËËÆ³Ÿ‰wf`bMD3+''*((.6\GW«°ª‰eX./903<8<5:94:>;=CF@<;?832.242025539.,*41CYv‚‡ŽŒ‰‡……ˆ˜¤¦¢£¥£¢¢Ÿ¡£ ¡£Ÿ¡ ¢¢¡§¥¢§££¦¢¥¥¡ŸŸœ™›œ–—™””’–•‘™’“’”Ž“ŽŒŒ‘’Ž‘’˜˜›™•••˜˜˜˜˜–™ggfngdfhbdf^fe^ac``ie^W[Z\egvˆŽ“œ¡¦¨«ª¨¬®°°°­¯³¯²¬¬­«© ”ŽŠ~sh`XTOHLQRV\SX`]^bddddghe`dlbiihfgbhiifikfjjklnmmprwxwyvyyxuw}uxxz|w}~“xLLar’ŠŽgr€}|„€z†ˆxws€{ohU;/3]nM'%"#'VZ??64$(=,:?<<3GmqB9F64%'.,;8)&'7kM'*,?]]:;;(+^u>(,(% )+Ix‡ue\eyš»°¡ ¤£¥¢œžŸ£¦¯¼¾Â¼·´¼·¸²ª®¹µ«©¦¦§›˜š¡¤¨¢¤¦¦¤¢¦£©§§¬«´²­°°°²²²´®®±¶¼½¿¿ÂÄÄÈÄÆÃÂÁÅÂÉÈÉÈÌÊÉÌÌÌÐÑÓÐÑÔÔÔÖÒÓÎÎÆ´£‰wd`[U@4+&***(.4QHNx¬«±‘a[66A29:>I;=?>;<;:EDB@9<971.082//575/3,,-4Khz‡ŒŒ‹‡‚ƒŠ“™ ££¡¥££¡¢¢¤Ÿ£¥¢£Ÿ £ ¡¤¥¤§¦§§¢§¨¥¢¡¡ œ ›˜––••“”–”•–‘Ž’ŒŽŽ’Ž‘ŽŽŽŽ‘’–“—–˜—˜•™–—˜—›eegggfgghccbac]aaaa_aZZVY]dit~†Œ•—Ÿ¡£¥ªª­ª­®±®°°±­¯¯®¬«© Ÿ”’ŒsmeWOJHKOMRVY[Yb^cdbgfggeaggfefhjijfhheihgkikpqnqjqvrsuy{y{u{zvvxyuvw…¥Žaov‡›§º¶}nzt}…|€†ƒ|qpz„ujV9/4BYzL+$!%,L…]QE')*47#4HLDOhxWIOH51'(/(8>1%+Dr3*(+?^r>6*-A}:)$-4.!">vŒiVYq¹¸Ÿ §§¥¨ª¥Ÿ¡©°¾¾¾½ºµÀº·­¯²·°­²¨ž••˜  ¡££§¥¥§¤§©§©««®¯´±±¯±²²±¯°´²¸¸»¸¿Á¾ÄÅÅÇÂÄÃÃÅÈÈÈÇÉÉËÍÌËÐÐÎÒÓÕÒÔÐÒÒÒÌÆ±¥‘jaUN;2'&''.+47TOGw¬²°”lY78K36;AJ=5:C?;9C<=@A?6.023A4363583-3*+7ATky‰ŽŠ‡€}ƒ““𢣠ž¤¦£Ÿ¨¢¢¢œ¡ž  £¡¢¤¥¨£¦§¦¢¥¦§¤¢œ¡žœ›™™’”•—”’–‘•’‘Ž‘‘Ž‘“‹Ž’ŽŽ‘“Ž““•’”•˜™š˜ddfmcad^gac`e`a`\]a_]_]XZ]eiwˆ‘‘™œŸ¦¨«­¬««¯­°¯­±¯±°³­­£Ÿ˜‹ƒumdRTOGNPORWX^_^^bffbg_adhblhhjhdddghigheggllkqnqsqswtqxvsxwyyywyrwvw†§¯‡‚‡Š½Á•uutxw€…†ztu€m[;-/:@Zx@"& $,N‡`d5&!%7%(/8HSfnVPVC>1,,--1=92,+Go/'(&8du=)&-]g*+082&$.dŽšs^Yh”´¹¬ ©¦°«®©¥Ÿ¡Ÿ¨±½¿¾ºº¹¿¼·²¶²°´¬¢Œ|…‹“—–žŸ¢¡£¤¨¤§¯©¨®°®±±²±³¶µ±µ²¯¶¶···ºº¾¾ÄÂÃÄÃÁÅÄÁÈÅÇÆÆÉÍËÊÈÎÏÌÒÑÔÔÔÏÏÏÍËÆ¶¥”j_RG<10'%'+-(5DD:y¦²³”uOGAH6?=RJDB03:;A@;5110440@37@881.*5:8@F:E>68132.3;//7396/,*-3BThv€ŒŠ†‡„‹–˜ ¡£¥¢¡£ ¡¡Ÿ¢¡Ÿ ¢Ÿ£ŸŸ¥¥£¥£§¦¦¥£¢¢¡Ÿ œ¢ž ž›››——˜”—–‘’’”““Œ‰Ž‹ŒŽ“iihencchcabfaaade]a_]]]_W_fir}Љ–ž¦¥¬«¬­­®¯­­±®²®®­­©¨¤Ÿ•’Œ€ui]VSJFSSRVXRXX^```adgdilfgidhfhheedgeidjmhkhpppotovwztuywvxuxut}—©¶™‰Ÿ83>CCNYbv†|}‚‚€yqm~q^4.2M^GwW"#)%/;Zj:$&"0725HMG?<2>C-(,(%*(-102:2/WG$$&,Tel<#$JmAEP;'!*E}—t[d|©Å¹¤¨§«³³·«¦šœ–ž§²¸ÀÀÀ¾Æ¿¾¿»¶¸£mMIRfunyy†‰Ž“‘”™˜Ÿ¥«ª®®ª±´°´¶´°³­­¯°±²¯µ·¶¸½½ÄÄÆÇÃÄÁ½¾ÃÅÅÇÇÊÊÇÈÉËÍÑÐÐÒÑÏÌÎÈŽ³ ’ycG=7302*(+(.%-HY,^ž²²›†_CCB86?A;DK@>@=ECOS>?79.1+741648>90,114FWr~ŠŠŽŠŠ…‹“¡¢¤¢Ÿ¢žŸ ¢¡£Ÿ¢ žŸ¡¡ ¡££¤¥¤¢¢¥¤££¥Ÿžš›Ÿœ›››œ™”˜–š•’–“‘’•”‘‘’‘‘ŽŒŒ‹ŽŒ‹ŽŒŽ“ggdagcfee_`cdddadbede]`YYZ_ks~‚‹“›¡¤¥¨©­®¯°±¯±°±°±±­¬¨§¡˜’‹ƒwkcWQPDKSQXW[`aZ``cghihifdagaYgedehc_ebghhmjkmkusvsvuvxyuvyywwuz–Íͽ¥œŠF$&%&&'+>n‚‡ˆ‚}ƒymirq^TE/>?bFZm?%#$+4Our+##&=239?:50//3@2(1)*+4,.5330,G7*))0eUl9)2[[72.)#;yŒ x_dq—ƺ©«¦§°¹¹·«£š›–›¤³½ÃÁÀÂÅÀ¾»¹°‰ODGTY]ejnhty}~}„ˆŠ”˜ž¦¦¬±¯µ´´·µµ³®²²¯¯²¯´·¸·½»ÃÂÃÃÁ¿À¾ÄÄÂÃÄÈÄÉÊÉÈÊËÏÎÍÐÐÌÍȹ®¤˜…pRA<702+&%&"%,/@_-P ¹µŸ‡^@85>@E9859==:;IIGFH=5233/332015421/3+2H]t‚Š‹‰†„€€†‘—Ÿ¡Ÿ¢Ÿ ¡ž ž ž ›ž¥£žŸœž¢ Ÿ  ¢¤ž£¨¡¡¤¡Ÿ›œš›žœ™››ž›˜™˜–˜””’—–’•–•–›’’‹ŽŽ‘Š’ŒŒ‹‹‹Š‹Š‹bbgeebgaedebccgeeca``\`[[_eguˆ‰“˜š¢¤¨¦ª®­°°±¯°°±±±±­«¬ª¡ž—“‹€zkaXXHJRNUXWTYZY[``fefjhgjihlegdfjgbdagjefkdghlsnroxyrwvwwxvyvwƒ¯äÊosV8%!!%%)%IŒŠ—¥«™|{pietjO905@YfAk_("(+':^j &+A,,.843:*45:0+/*,5:7.1874.JF/+,8`Il>03`B+% #+m‹™‚c]l´Á«¥¬®³²¿À»­š™œž¯¸½ÀÃÄÆÅÀÀ¿ªxUSWX`a`fc[`hddVfntz}‡œ£¦­¯³³¶¶µ±²³°°¯µ­ª¯µ¹¶º»ÂÂÀÁÀ¼¿¿¿Ã¿¿ÃÁÅÇÉÉËÈÌÎÐÎÎÎËÆÇ³®¤†pdL?;6-+&'(('**/E`8>›»µž‰xR353;GMC15B89B>FDE;9.4/2/11275696/*14;Sex„މ†„~„—¡££ ¢ŸžŸ œžžž¢¡ŸŸžœœžšžŸŸ £Ÿ¡£¢¢¤¢¤¡Ÿ›™šž—š›Ÿšœ™˜˜™—”•“’•—””—”ŠŠ‘Œ‘ŒŽŠŠˆˆˆ‰‡ccgdeddedhgdiacdheeab]\\_\cgv~ƒ‰‘—›Ÿ¤¦©ª¯¯®³¯´³±¯°±®¬¬­¤ ˜“‹yl^WQJMINYSXRX\\c^ad_ahegifgiigh`gicjeiiggjimmmmquwxxutwzzx{yyx¶ß·l]455,''-')8a˜®ÔÖ¼{vjUQlWF214<\EXƒI)(1*,G`~ˆq""((6.'00/8784?J93)0/69-+&/HFOZR2==4W3iN&/\5'' *L„¦ƒl\dˆ°¾­¨ª¬¯µ¸¾Â»©—š˜žŸ¶»¾¾ÂÄÂÁÂÁžvfhmvxxwulf`ge[SMWU]fkr}…’˜Ÿ§®°¹µµ°¬±²±±¯¯­ª³µ¸¹½¿Á¿¼º¼¿¼ÀÂÆÃÆÉÊÈÊÊÍÎÎÍÌÉÁ¸µ¨˜‘ta_G=9514+(&'('&1A\75}º¹£{Z:;-<:>:9@@L>B565./-2407:17441++3BUj{…‰Ž‡ˆ€Ž™œ¢££¡Ÿ¢ŸŸ œ¡žŸ› žžžž¡ Ÿœ›  ¢£¢ŸŸ¢¡¡£›š™™™›žžœ›šš›œ›™š™˜š˜˜”•–••ŒŽŒŠ‰ŒŽ‹Œˆ‡†‰ccfbfeddhdegjdcadddc^^_[XZbfr}ƒŽ’—š  §¨¬¬¬­±°´¯±®²²±²°ª¨¢žœ“Žti_SPJNPPOXWUXaea]cgfgdheiedifhmljggfhhhe`gjiijnsystuyuy{{z}yz}†²Ñ®‡d364/-&(0;U{‹–¹ËÀºoYIDTOA74?AaHx,(-')=Ydx—r'!$+4#*45u—Ÿn[`{ª¸­¥¬¬´²¸ÀÂĵ¥šœ˜Ÿ©¶¼»½ÂÃÂÁº™yvz‚‰‹Ž‰yunkd[P??EGJLYjozž¥¨°³°¯°²´³´°¬¬¬¯²´ººÁÄ¿½¸¼¼¾¾ÁÂÂÅÈÊÇÇÈÌÍÌÆÃ¼®¦›‹zoi^UC>98/+')&(+),0?=;838;15,:8405<<411,2/F`lˆ‰ˆ†„“˜›¢¨¦¢ŸŸžŸ¡ŸœžžŸžšžžœžžž ŸŸ ¡ ¢ ¡žœ›•ž˜š—š›—›˜œ››–ššš——–™’–”–”’‘Œ‘Œ‹‹‰‡…ƒ…„…ddiidhgbgef`hdaecc`fa^^[U[]mt„‘”™ž¢¨§¬¯­¯±³¯­²²´¯±²®ª¨¥œ–Œ…vl^SSKGJOUTUS[^a^^_^eepfbgcccdhhegdfjgfbfiiilgpnsvrsowvvr~{{‚€‹¢´œšoJI;=-&')/V~Œ¬¡ˆƒ†vlVA>XSC==6W][‚H+(.%4WY[Vo)!!('&*=D83((,(+*/1;D9-y¬¼©’Œa5<.87:H?HC<;F@>;;847<41,-109=67561-7L]wŠŽ‹‰ˆ‚€‹’¢£¤¢¥¡œŸžžžœŸ¡œ ›žžšœ›œšŸššŸ›¢œŸ¢ œœš˜šš›ž—šœœœ›–˜—œœœšŸ™›œ™”šš”ŽŽ‘Ž’Š‹‹‰„†…†…kkelfkhfhh`ccdgjedf\b\^^W_`it~„” ¨§¬¬®®ª­°²®¯²±°±­­­© ž•’‚{mcWMILLTPX]X[Yc]^__fcngdlehcceb_fehebgfddefjnomppruovvuzw{}€‹ˆ†‹•˜¥^F61-$'-0P{ˆ’n\`m_@?I`W>68B[OlT.0.6-Fa^U;."%(''3M?B:,?S*3JT4)6>*(#+( ')4PfY=ia%3]:/@#('*P…¢rdsš¿½¦žª¯²¸»ÁÆÄ³¦›”œ¥©³»½ÀÅý±ˆvyƒ‹‘‘˜™™—’¢›–•zm`]RFMCA?9FOYg– ©¯«©¬ª¨«ª«°¯¯¶¹¼º¿ÀÀ¿À¾½¹½Áþ¿¾ÃĽ½»²«š‘rjXOTBKUVZ^XVVVG6/.1,,+0/8@;.u®·¥“‰u@>485;O=CPEABGA=75+-512/1--39:82161:Ol‚‹Žˆ‡ƒ}‚„•¤¢ ¡¤¡   Ÿžšœœ ›¡ž¡Ÿž›™››™›œšž¡žž¡ ž ™›œœœ™›››™›šš—ž™™œœ™šžœžšš—˜›’•‘’‘‘މ‰‡ƒ‡…‡…‚hhfifdfdhjghhbcbieec`_^^Z[_gn|ƒŠ“˜•™£§ª¯«¯­­±¯®±°±²´°¯©©¡–’‡~i]SQNTURSRXW^V\bcceahljehfhfdacae]efclecffmnlliporturysvu‰’ŽŠ‡‡¦’HA80,#&)%R€€‡hZX\bI=L[T95B`_QZ50**'E_]g@0~„? &$/3EaLG<7=cF?[\C'5;))(*'#(#-3O_fvT&-[Y/* $9uš§ˆnh·Ã¬¦ª¬´´¾ÂÆÇ¿¬£––¡©³¼¾ÄÅÇÁ«|txz†‰Ž——š•Œ{–Ÿ£ š‹…~~sacWSHHD??AO[yˆ•¡§«§ª¤§««­¯´°¹¸¼Å½¿»Á»º¼ÁÃÅÁ¾¼Á¿¹°§›ˆnaXXVNXPUTmovohjkaT?3.5*,&'+1FA/y´¶£‘†vF>>434VB?DA=DC==641,72064/485C877/8I?E349/.B53=4144<567>=Jhx‡ŒŠ‚…‚‡‘˜Ÿž  ¡  žŸ¢£¡››œžŸžœŸš˜œ™š™œœ¡žžœ¡žŸ›Ÿ›—™›œ”–œœœ˜œœŸ›˜™šœ™ššš—˜–—™–˜“”‘‰ŒŠ…„†††‚cchceheijlckfkcbebc`aXZWSU^jr|€’”𛤧¬«¯¯­±¬®°­±¯³´¶µ®©¡œœ•Œ…zrbXQKHHUSXX[Zf^^bgiiegccegecedfchcdfhahcccdhflomenmkqpz–šœ›™–”’‹°¸‰yVl¥ŒfPGg„}|”vW;YR71+3:HSmxeGA?DKY{c7#$&?qD0/)*DRq}‡ŠŒ†ƒ~}ŠŒ™¡Ÿ  Ÿ¡ŸŸœ›žœœœšŸ›ž›š››™›™šžš™›ž¡Ÿœžššœž™š™›š™š™›™—™›œœžž›ž ›–™‘–˜—›–•”‰‹‰‰ˆˆ†…ƒccdighfijiblfhedffa\^ZU\X[ahtƒ’”œžŸ¦¯­¯¯¯°¯±®±®±¯±³­¬¨¥¡”‘‰yp_\PKLLNTSRXZY]fbahdfidbhhhfbihcea`kfecdgjfjhniimnkkts•—™žŸ›–™™•±Â³¬‹Œ¬ ˆxxƒziu‚ƒn<7;>(3:GMRZ{q=FCMHPesa,""#7[kW,,1*0[W5/*%#$0Mglb4(+%(&*22.-&,*%(-;U&"-b”¡shkŒ¹Á°¤¬§±¹»ÂÄËÈù±¨¨¦²º¼Ä̲uUd`hlke^_bffiljW>/Eo‘„|SDepIhn|}k_utomqpfmyŠ“›žšž££¢£¨«²¸ÀÄÄÄÆÄÁ¾»½ÀÀ½·¬¤š‹|zzzonvp_FWKEZ_U]NKMQJC>AM9*)'--9BL-dœº®‘€nc_.-/37^;>D?A?:;23,--576758740.25=H^x„‹‰†€€€‡”Ÿ  ¢  žœ›žšŸž›šœ›œœ ™žœ›ž™œ›¡š›žž ž Ÿœš›››š›š˜›—›™˜›•–š›šœ ŸœœŸš—š–”–˜•™™—–‘‘‘މ‹‰Œ‰†€__ebdcgmhkelbee_dba^^[UWRZ_gs}€’™š¢¦¬¯¯°°¯­¯°°±°­°°¬¬¥£ ›–Ž‹xicUOPJMN\UTTW\e^\adceibffecc`aiabbhid^aceidbdjghipqy…‡‰’–žž™–•–“¨¼´·¬¥®§›Ž†fVRU\H:,dx`% ""1TRn10--_`lW4%0+*+--4*.++*.''D?#?‚¢ji|°Æµ¤ª¨­°ºÀÅÈÈÊÀµ³©®­·½ÆÊ®eTU_b`dgd`JKICPHP:6/8Qi^K0.CE@Vpz€We€wzusttwz€†Œ“™œ¢ Ÿ§®°¼¾ÃÁÃÆÆÅ¿½¿½¿¾·©žŠˆ~ˆxZQXI76799AG@AE?ELFBDIA..*%'.6HL-\œ·±“tpgW0107;T<=CAC<85/4.4.2.7036;60+*4?Wl}‰‰Œ‰‚~ƒ’™š  œ¡œŸŸŸŸ™šžœ›ž›žœšœ˜™™›™›œœžžšš›™˜œ˜š™›˜–šš˜™ž›š›œš™›œœ›š—•—’“‘’–•””“’Ž“‹‰ˆˆ‡‡iijfjebmdecddccdjb]]^]TXQX_dt‚‹—™ £¦«­°²®³±¯²±±°³°²¯¨¦¢£—–†niUMHIJMMSVXU\`_gccdcdfiidfebddgedaa]__c`ddgbihow‚ˆš”•–›œŸ›™–›¥œ•Š˜¬¬™{O3C80/,.0;V\9KLR]r{a1,@IYWKhtO)!!"5]@h@')-6Y]+(&&$).0C\v`:*),.1-(/-.3)0&$#(=!"(oŸweo¢¿¹¨ªª°®¶¾ÃÊÌËǼ¹µ¬±·¿ÆÍ§^PRO^WMEFWT@@C>7;842,25<78/+/009S{eaxˆ|yz{|wz„ˆŠ•šŸ¡šŸ£­°¹ÁÃÀ¿ÄÃÄÂÂÁ¾À»´¢™Š‰ƒgm`G;9732414498<FGB@FN3(+0($+-HS+Vš¶«›pwrSD42/<\A8=E;87340120122.3145+'+5;So…•‰ˆ‰„~‰‘™œ¢¢¥ŸŸ¥ žžœ™™šœŸžŸšœ˜›š˜™œž››šœœœžœŸ™œ˜˜™š›–™›Ÿšœ›—˜š›šœš›™™–•’–’“”‘•’’ŒŽ‰Œˆƒbbgceeebecbgbabffd`^`UYTRS]jv~…Œ–™™ «§«­­°²®²µ±¯®±³°¬¬¨£ š’Œ„|peUPQTHJMNSWY[]\\bcdhcdhiehfdce`ad]_digc_d_badq‰•ž›Ž‘–•—šš˜–•›—š˜Œ{i{’›‰yM,1,(!(%(<:LJRYVlˆwH3;BQkPMktL+,(*9a8PX/,*/]e/)&##&10:I}xeA/(%)+,9//0(&$$'%#(O‘ ˆnf¼À¦¬ª­°³»ÂÆÊÇÉ»¹»¶¶¿ÅÉŸZHOVMPHG<4;@86952712535521-+-+)(,6FLjŠŒˆ€€z…ƒ}z‰‰ˆ‘’—›žœŸ¡©¬²º¿ÅÂÁÅÅÅÆÆÆÁÁ·ª—Šsd`DF81275811301/:7<68?@IAG;.-1*$&'-HT.Lœ°±¦|soTPD02:SD=AAC8=10/3192..1.795/)'.:_v„Љ…€†‚‹‘—¡Ÿž žžž¡œžœ›ž›œ™™œ¤Ÿœššš››œž˜›œœŸ žŸ ž˜›”œš˜™š–›œ—œ™—˜–˜›™œ™šž˜›š˜š”“”“’‘‘ŽŽ’ŽŠ‹‡Š„ccihgehccccgcddd[]eb\U\ZPUVgr}‚‰˜—£¦«­­¯²±­²°³´±±°¯­«§¡Ÿš„znaX[LIKKRTWVRY^\_cffjeggecddcdfbadae`_c`]^^b_jƒ›–¢º¼‡ˆ‘”••˜™›™”œœž™‘x`cfy‚^D+*- !*BVh^MP\]„ŒiU==D^iGUsyR*(&*1d56JG-01cg.+',$(&'+0\{|nO2-*-'+)/.,%$,3:z¡™€m}ªË´§­³¯±·»ÄÆËž¹·º´»ÆÈPCHGGDC?:536434412/10.2;0896A,.(&.*+8^ƒŠ‚‚„}~ƒ‚Ž‹‘—™œž ¢©´·ºÂÅÄÇÈËËÇž¹­Ÿ„vW:=4253243310/.467:68877dyŽ‘‹Šˆ‚‚†–›¡ žŸ¡žž›ŸŸ›™œ››˜—˜›—š ›™¡˜›œœ›ššœšœž™š››˜™žš—™š›˜—•š››˜˜™™™™›—˜––•‘”Ž’‘ŽŒŠ‰‰‰‡†‡ccigeb^bagdd`^`]^^b\]YVRRTRbn{ƒ‡Ž•›Ÿ¥©©¨¬³®°³°±³²³±¯®±©£ ž—•މyncYYPJGJUSTTZY^WWea^eijdhbebfc_cf``dc\_\]ZZ_ew– ¢Èξ}†Ž““–’–™™›™›•}kRF;OjeD(&$$)?eti[NPYk‰]XdPFLlPIa||U&&.'0_>)7V:/2Ys/'*($'%% 05pyqr`@&%#'+/34* &*&Z‘­wg{šÁ榬°­¶´¹ÄÈËÃÁº¼¾¹ÂÆ¢XFAAK<67952544:3/23././,37?TG73.(.'(0(7Xoƒˆ‰€ƒ{~ƒ‚Œ‰’””œŸ¥«´»¿ÂÇÌÇËÊËʼ²ž€[N;8;079719>:10042377;A657@?32.40,-$,0FE/F”§µŸƒof^GGJ14L>@<@A54/,0/141-998285++',Jg‚’‹ˆ…‡‡•œ¢¢¡¢¡œž ˜™žšš›š–—š˜Ÿ–— š›™˜˜—›š˜œœ˜››šœšœ™™˜—œ—˜œ˜šš›š˜›–—œ™˜™›•˜—“’”–”‘“’’’’Ž‹ŠŠŒ‰„]]abbc_^]e^^`^\_]^Z[SPRNNUYcmtƒ‡–››¥£«ª®¯²¯°´²°´°²°±­«¥Ÿ—’’ŠzodSMFEFOIQVUWSZUZc\^_ifdgbf_bc]_dbaa_c]\VY]a|¡²¦ÆÕˤxˆ––“˜šš›–™”‹„uiO30JRJ5'$#"(@ZvtgNC=ec3AfUNNfEOmuN#/*6X=#*?U=-^‚9,-*(&$$)%*AiyyrgG&#)&'-/%)'"!#E€¢•ed¹Æ¯§¤¬±°¸¸ÀÃÌÉÃÂÀ½¿¿½Å§^JAACC:52/05535735-+*.-/0OBbwo\F9.*'/)'.;2/048A<9<9;650--.*%,,3ED.E•¨·¢„ab^J@>?7TF;;=:41///34307.12>51.)+5SmƒŽ‹ˆ„…‡’™Ÿ ¡ žŸ›œ›™——™™›—šš›—œš™ž—ž¢™™›žšš›Ÿžœ™›š™—››–›™šœš–˜—™™•–”œ˜›–•™™•“‘’‘Ž’‹ŠŽ‹Š‹Šˆ‹‹‡‰ˆ‡]]\]ba]X\]\a^Z[]^[_UYSPLMNT]ms‡•—œ¤«ª®­°¯¯­¯°¯¯±°¯°®¨¦ ™“‹{rbUNFLHKNSTWYXX[\`\`__bgca]^aa`_aea__YVXWSUm®ÎÁ¯ÐÑňy‹ŽŽ‘–•œžž’Šˆ‰‹tW73FN;/(!"!&4xm[F2/T~_F79ZXN^ZEQ„“pL*#%2nzpmeR:+''-)""&!!3j“§q`~¬É½§©ª«¯³¹¹ÃÉËÉÂľ¾ÃÁŦYCKI?E^€¢ÁÀ¯•wb;+'&(*,3Qn‡~}€~†ˆ…Œ“•˜™¡£§¯¹¿ËÌÒÔÑÓÏŧpA123-.*0;9Qq\K–ˆs_@.')+0786:77.)042.*$..O@-B“ ·¨’`SfLBA;?VP=><:302-032402063022,26Lj~‹”‘‹ˆˆ•™š  ¤žœŸžœšž›œššš™—˜—š›˜™šš˜™š˜—™™œœœ˜›—–š™ššš›ššœ›š˜˜˜•š›–—““——˜˜—““Ž’’ŒŠ…ˆŠ‡Š‹‰ŒˆƒŒ†„ˆ__baab^]^Z``[[ZYZ\YUOVZKLOQ\is€”š¢ª¬°¯°¯²¯±²°¯³±°±­«¤¢œ—’Ž„{pdXRIILLQTVXY]\[``d`bbafcb`cgb]dc^`^ZXWXXTW‡ÍƳ½Î¶Œz€‰‘•—•š™‡e\‡ƒoPMV7-#"0/,*Y€sa5'.7mwD%%*@Uirc]d}yI8.-&,*?<),0-.0G€f+0/11.+2/+-02;;-2=OZU2*'"!!2r˜k`y±È°¤¨¬³µ¯µ¸¶ÊÌÈÇÁÅÅÅįiLPWULE<2+)*35=MC+/><-'#(WWAVy®ÊÌ¿°£šaF,,;LJ?Om{‚|yvy€‰†‹Š’‘”™ž¥¦ªºÂÊÏÖ×ÕÕ̵zQ621,+1,,77[”€E¡¤—‡kP6++++5868:5/30/2.%&,.O?+7‹¢·©“_YcXEA@?XC<=E<51-,414578400/.,+0@]q‘“ŠŠ†ˆ‘˜œ¡ ŸŸœŸœœœšŸ™š˜™œœœ—›™™”˜™™š—™š˜›š™ž˜”™˜š˜šš›™™—™™œ—œ›šœ™˜˜˜–””•—™˜––’Ž‘ŒŽ‹Š‰Œˆ…„„‰ˆ…„ˆ†ˆ†``eii]]^b\]b]]aZ[XYRQPQMIJS]kx‚‡Œ˜™¥©­¯®³­­±´±²¯²´´±®­¦ “•‹yqbSHJMINRXYY[[bebfaadf^_d_ega`cdc]]\\\ZWYUU~Á¯Åʼn~„ˆ‘’™—™ššƒ^m‰ŒiB?G4$&+833PucN=&&3_rM2&%+,H^|xc]Zvp7/*)(G/.0''8.',>z}?-5+,(*+)30/1./)*,@HA8:%&'!%R¡~`lœÆ½¤­¯±·²±·¹ÃÎËÈÆÁÈÅË·kQRXUND916,*,3ARZQ2hlTKd{|xzwy€‡‚†‹‹–‘š £ª³¼ÇÐÒØ×ÖÓÁ\C225<40++<9K‰L–°Ÿ’{^C8-+-2213831.22/.&'4>PG-6‹¤²¨ŒeeVl@?I:_B=?;86-.//0+6=3;2.00/1:Jax‰‘‘ŒŒ…’šžŸœ¡ žš œžœ•—œœ™š™˜˜œ™šš—˜ž›œ™˜˜š›™—š˜™šš™›™œš›™”•˜˜™˜˜—•’•’™•”–˜—‘‹ŽŽŒ‰ˆ‰…‰‰‡…ˆ‰„‰…ƒƒ„‚bbcb_f\`b]a`[^^[ZWUSQSQJHLQ_px…‡‹•𠣩®°­´³²¯®¯³°´¯´³µ²ªŸš™Œ‚{k_ZMILNQVWS_`a`c]^dcde`]eb\bcfaf`a^^[\ZVXRI^ À´¾Æµ§“ˆ‰’“—–”–––g‚˜š†H-LQ+,%&BHUm\>.0&7MfG@/($)2RVƒwWZR‚q545.*.,,%$(5+,,4fJ2>5)')(../,:22)**4:4*)/IK9;t˜Žg]ŽÂÉ´¤¯¶³¹¹¹·ÀÌÏÊÇÈÉÄɽzP[^VQD8954*.48F\gZ29^]L/(*DpM8BÌÌÆÆÆÁ¬cGE]ƒnT[tzutqsˆ‚„ˆ‹‰Œ™œ§¬¶¿ÇÓÚÛÚ×ÌžoVE82AC?6-&<+4ˆ§¯©”rYWfO@DHU??;:631,/9=1.147-*+/25BPm{ŒŠŽ†Š‹™›£Ÿœ › ››œŸš—™—˜˜š™˜—˜œ—Ÿš™˜—š™˜—œš——–˜š™˜š”—˜—˜š—™™˜—•—””‘‘‘“–•”“•‘“ŽŽŒˆ‡ƒƒ†††‚„…‚ƒ‚ffdb`c]d]`_d]c]\]VUXSSOJGLW^mv†ˆŽ”™ ¤¨­°²²°°­±´±µ²¶±±²µ¨¡Ÿ›‹{qbTNGIJOUVV[[ce\dcdcgfedacceclffdcea]\\[TC:L¸œ•®ºÂ°¤—–“š–•••yi’™Žf1)+*$&)-Kpi]E*%/0NY<7E;-1+;HXˆ_Ba[‡sB470.+.1)(5*0&'.RˆT6E00#$&/&.78/+)'282+%(-5HJb‰u`tªÍ¿ª¤­ª²»¶½¼ÈÉËÆÅÌÉÈÂ]akeaVS@6312-/BG_skG4\bZHBBZlJ5W±ÏÌÌÎËÈ¿«XFX|x^Zlwrpqsx{…‡ˆ‹‘™¦°·ÀÇÐØÚÙÔ¾eNON3`;+;„«±®–{HUhWFEWW7;B66230210.31201+.078AXr€‘‘ˆ‰–š¡ŸžžœŸ  ™›™–—–š—œ–—˜™™—–˜—˜š™™—™—˜—™•š˜–™›˜›š—˜š––›“”–•“•””“’’’ŽŽŒŠˆˆ……ƒ†‡ƒ‚‚}„{€‡‹Žcccbbj]`cd]d_Z]\[VWYUTOOIJR_lvƒ…Œ—™¡¨¥¬±°®°±±¯³²µ¶²¶³­®¨¤ž—‹ƒ{pg\PHHJNVWS`b]icefbfegadkheegkbbbff^``_WF22J—¹…USl¢¾Äµ©–—”—™–‰]msVA%'(,'"/9ux[L7)'9NWI57LN0/2;Cf€cNQe•p9/./-*/01,8.+)+1EzxF@,)*,*-,*0:.*&'()+%'#%%.Ji„rVl“¾À¨¡™•™¨­³¾ÃÊÊÊÅÁÆÇÇ“ibgtni\TRNC;.+-EJ]uycC:TbTT[SH9B~ÅÑÎÐÒÍÎÈ·•mLFl~j\`ppmpvyz|…Šˆ•œ¡¥±¹ÁÈÔØ×Ô˨|ZUga==^dYKCWiUK:W¢°¦tN>;-,+(+-/1/2)-1*'+,44/-3347:754.-.0-6RhŒ‹ŒŠ‡ƒ‹•—™›šœŸœšŸœ–˜˜˜–œ™–™™–—˜—–˜šœ˜›šž™˜˜™œš™œ•—–—›˜––“”Ž“—”—Ž”’•’‘‘‘‘Œ‡Œ‹‰ˆ€„€†€€‚Š‹‘“›£ ii`dcc``b_ceh_\\\\XUQPNJDGOWgx€„•š £§©¯²¯°±¯±´¶´³¶°²±±¬£ —‘ˆ‡{rd\UNITSWR[Z`b]aefilkd`fkkl]gkhlirzkUGHA:0*+?š¬qn‰–­ÁÕÍÓÆ¶­šl9*(*)-1#$'/0=]aRE8730=CR>J8=DV:8>HUƒˆvaW™u@.'1*04//6@.,(,(:TuM+,405,-++540*/+''+'.(%,@}ŠgQfЦ¢’—£¨«»¹ÁÆÅÈÇÅÄÂÆÃ˜lcprz~}tuyoeaK2+3D\ir}qY>96<9@Mk ÆÐÎÐÏÑÊÈÀ°ž„saf}}tforw|uq{€„‰ŠŽ“›¢©±¼ÌÕÖÛÙÖÀ†hil€†vQAUgd_bQEGo¡³­¥š‡mRE=057).,/-(/.(-(',4Qe1-5j°§¹›„W?[WLIVO86572.?:0511><52,1-/15Yl‚’‹‰‡‰—™žžŸ›ž›˜šœ—™™˜—‘—ž›•—™›˜——–™š˜”—›˜˜–˜–™š•šœœ˜—–•™–”“Ž™”””—”Ž‘“Œˆ‰‰Œ‡Š‡†…ƒ|€ƒ„‡’•šŸ£§¬eedc_dbbcea__`ZV[^V\SMMIEKVZis‡•𛢍«°°´±³°±µ¶µ·³¶²±­ª¢¢–’‹}yqg^WQPMSUZ[[aiedefjihiiggfmecglftkN928:2-41Fˆ«†Š³Ã¾¹ËßîâÓ»y@A*,*/)))&)14Ta\?88ED<8>HUNWPXŒvhXQt‘}H5$(-:0029<5(.*.4FO=*)1:2-,(--68('&$!%*025?n•|SV~°¹Ÿ™©°³´¼¾ÃÇÉÊÄÅÃÅàmhquvv‚„‚|‚|ulWA//Icqpuxq_R?AKXž¼ÊËÊÊÎÌÆÃ¼£”Š„xjt~zpps|}xvzy…„‹Š”›¥«¯ÁÏ×ÛÝØÐ¶cor”oZMKYXUKLsž´¶±§›‚jPA522/-0313(..++++-3P_:4:hª©¹›‡\>V]YR\P;;886.0.7533?:50-,,+063066350.48330[X619[«¿£ˆgEKdNaSNH81-12-2-,057::/-2/+3Mg‹ˆŠ†‡–šž¢œœžŸœœš›˜—š™˜›——•˜—–˜—›¢¥š™”–™š––›—œš˜š—˜™—”•––•™’–”••‘’””ŠŽŠ‡‘ŒŒ††„ˆ‚ƒ‡“™› ¢¦©­­²²µ·ºcc`effbc`ceac[[]\WZ[VOIJKJSYk{‰Œ“šž¤§¬­®°°³´²³¶¶µ´²´³®¬¤£š”ˆ…rg]VRTURX\\Yceagjjmomiomklr|j@8/**+69J~xx„™˜›¨¶½«žµØëÚ½‚S=,3:nW*('-./A[yƒ€skV/,/&#(Bd6>YaTMAWMJhvx1Ot¥ƒi[GK2-.%.9,'$')14-1-0<@:6.,02D;/4*#'$",);n‘“_`|¬Î¿ž¤§®´º»ÅÈÊÈÆÄ¿¼`bvy{{€~ˆ“‚|tkciSKq}‡Š’•œœž¨­°»»ÁÄÁ¹¯§ªœ”އ|uv‚€†~}xwy{€„€„ޔ𧭻ÃÐÝßàÕʦ~tf`\lwŽŽ‰ŠŽ— §®´®¤ž|f\M5.5C@>885:810262336[V888\¥½°‹oSU]TeG?>35-.)/,-,1/758.+(0/CYu„ŠŒ‰Š‡“›œ ››˜šœŸ™—™•™š™–™™“•™›š˜šš™¢¢œ–›™›–˜™›š—›š™˜–—”––•™““’”‘‘’”’ŽŽˆ‹‰†ŽŒ‹‰‰…‡ˆ†„„ˆ˜›Ÿ¢¢¥¬°°±³¶¶º»¿hhghggd`cbc`bcYY]Z[T]PLKIAQYiw‚†Ž™˜¤¥©¬±³³³´¶²´··µµ³¶¯ª¥¡Ÿ”‰ŠqfaYVSXUY[\_dicfmnloqjnnrgz¡U.1,+.2;Fa‡›¡ž•Ÿ°ÄÀ¥ÍÍ´“S82.0.4Td@.3:Jg{…ˆ„h]l2$!+ #1Mn(7Y[ONHN^Pc‹†x7T§utpJA2FR$2+($%+%,/5221182.901/*8/'%," #,R„ŸmVl›Â˯£§®¶¹¼¾ÈÉÊÆÄž”a\lw{|{†~}„““‘އˆpmrilxrot……Ž—š—ž©§¬²²ª±´»½²¡›˜”ˆˆ‰„ƒ~ywysx‚€y‚”—©¶ÁÏÛßßÖÉ©š“|vyuŒ˜““——œ ž—‹†€q^L9,0?KC>98;840/1J7289[U/05X› ¼µ’pULWYlC6DAG/.+/:-+24;5::1+6BLbv‡Ž‘ŠŒ‡†‘’—¡Ÿ œ›™œžž™œ•–œ–š——–˜•——šœ™¦¨¨–™šš•™™šž››——“™˜––––”“•”’“‘ŽŒŽ‘’‹ˆˆ‹Š‹†…ˆ‡‚}‡‘™¡¤©«­³µ¶··¸¹½½¿kkggccgbahjc`][\[WURTPMJHHO\et€ƒŽ•™ž¦©«­²°²²´´·µµ·µµ´´³«¤£”‰{pi]URPPV\``b_fikkjpqmoppkmª>%.$*7?Nc€™œ’”žš¤ŸšÄ³‰M/4/+*%1Hq_P_kt„…lSIG^U+$'$()5]y-4aYQKONN]^•{Re‚©pos@8:`I+/.0*#)*/,7-0/00+*+2,:-9($%"$#""9wœ‡_a·Ê¹¦¨ª±¶º¾ÃÊËÉ¿Š^_huw‚|‚Ї‚…’ކxv}~~lmk|o`y…ˆ’–‘“§ Ÿ¢¤¯­¬«¯­Ÿ”Œƒ~€|t{‚{~‚ƒ†’•«·ÀÐÛßâ×ɳ­®¥Ÿ …ˆ‘–Ž…™›ˆub[NA2/8TSLF;75>94)1H7649SK,.4S•œ¸¶[MNYfJ7DB3,2220/)(2:<91.6:{Xly‡Š…ˆ…‰•žž››œš™˜”™šš”–“–”•›˜•˜šž¨² –˜—™“œœ™œ–™™™™•˜˜“•””–”‹ŠŒ’Œˆ‰…†‹Œ…‹‡ˆ…„…Ž—  ¦¬¬±±´¶·¸»¹¸½»¾¿hhhjfjhelkfa__YY[XWQPPRQEIL[h|‰•žŸ£¨ª«©°²³±µ´µ²¶¹´¸²´­¥¤œ”މ€qf]VSWUYZ[`dbgijfqppmuqqnl›A**'2Lbo~Ÿ¯šœŠ~†”¤º¼tC32<:7-.8Gq€njh[WE5;Bdh4("%%*2=`z'5ZbZH6FFVeq”{he¡mNoEFl_6&--,&*-.+0/,-/1/003293.+1$%!# *^‰œoYgœÀ¢¤¨°°»½ÂÈÎÆÅÆÂ–``imux…ƒ†‹„‡‰‰’“‘“’‰†„ˆŠƒzmhw}dXu‚„ij€seŸ£¨§³°°¨žˆ}w}x~|‚‚†‚Š–œ§´»ÎÚáãÙ婬§¤Ÿ–”“—‘ƒ|zq€tp~X?B>=?@C?CX[UF78794/-07456CSP402T—™´¹“‚^UEUcS@9<-.2/707/3;32;QMT3,6O‘šµºŸoZ:[cN>2;433561'/6>@1--64EepŽŠ…ˆ„Š–ž¡›žŸ¡ž™™›™›š——š™–š›˜™—–𔕗˜™––—™–šš“–”˜–—˜˜–•”™˜—’’•’‘ŽŽŠ‰…††„ˆ…‹…„ˆ‹–œ¢¢©­±²·¸º¸¸À½½¾À½½»»½»¾gghigkjd_bgb[YYVTVWVSRKJGHGUft€…”›¥¦«®¯³µ³±µ¶µ·µµ»¼´³®§¦–‡€ukeWLOWTU[[`dgkmimpqrsnmonpŽƒaouxЉr|„“—‘‚sNAJYTQZYI981'*,0.%'yœsko–Á²«¦ª±·¼ÀÊÌÏ̶`GQ\_gnrzz{‚‹ˆ’”™šš˜—“–Ÿ”œœŸšš•—‘Ž’‘“Ž“‘’˜–œ ¡¨¨§£¤¤ž¢£§ ’†ƒ…„„†Š‹‡„|ƒ|„Ž ¥ª¼ÈÐÕÞÞÖȼ»¸­¨¢£œ–•”Žˆ„€~||wsotlhmnmhfUQ@;D;/)*96-===FS365FŽœ¬¼¦‚†_9:^B79,8:/83-12?;7-'()BUs‚‹Šˆ‚ˆƒ‰“˜š››š˜˜™›–š––•“š–™˜•™–—–—•–“––—™™––•˜š•™—•‘’‘”‘ŽŒŠŽŽŠ‰ˆ…†…ƒ~„–™¡ª¯´¹º¼¾¼¿ÂÁÁÀÀÀ¿¿¾½¿½¿¾½¼½¿¿hhbccb]a^]Z\YZWSQTTKMFA<76?M\p‚„Š“•¢¡¦««­®°¸¶´·´´··¸¸¸°®¨£Ÿ™ŽŠ‚yh\QRUOS\\^[_bfkhmmpmohmkonmx‘œž®¨–vtwoaH@=;CNjdYN62((&:=\<1;GC7O`bijsjk_dF:4'*,#(=>LIF56OvweKXa`haXXj;?Z^<9?SjgT9=?7)%./NX;8AW\XmeuMCJGAeiQI/,+$#586?VS402Os‚x}yvqJJln@@_‡G&+10,,,-/-',3(+*067-32100&'(,Ež„iek‡´­±´¶»»ÃÃËÏÍÁ?ET`fqporx~‡†‡ƒŽŒ–”—œœœ›Ÿ¤££¢¤ž¦¡¡£ŸšŸ›¢¢Ÿ¥¤«ª£¦±¬®¥¢¬¬¡£«¤¤•Œ……ˆŠŽˆˆ‚~ƒ€}Œ˜ ©´¿ÑÙààÖÇÀ»ºµ¯ ™žœ—–—‰‡‚…‚ztwqvursl_VI>I94$-670@;>TK3,4?‰Ÿ¤º°j=BW]./91<-*&(11;,-,)00Lf|‡‰†„„‰Œ”šœœžšž›Ÿš—–™˜™˜–—–˜™š–˜˜˜“–”—š••”••”“™•“˜’•’””•“‘“ŽŒŽŠ‰‡€…‚†ƒ’œ¥­±¶½ÀÁÂÄÅÃÃÀÁÁ¿ÀÀ¿¿À¾½¼¾¼½¿¿½¾ff^`\[Y]\_TXUTQMPSTQIFF@>?BKXk|…‹–˜œ¤¦©¯²¯±µ³·²·µ·µ¶¶¸²¬§§•‘„€tgZZPKOR\]]_dbgoippnlljrkmmnuuƒ…„plhrolL?;JGP]o`B00<2++1=VSG@88*373:@<TU-/5192/(,2464*,+*6Pk|ˆ†‡…‡ƒ†’—Ÿ›™šž—™™™˜›™—™˜˜“˜—•”˜š––•™™–•“–——–•’‘“‘‘•’’–“’–ŽŒ‘Žˆ‰…„ƒ…„ƒŽ ¦¬²¶¼ÀÃÄÃÅÄÄÃÂÁ¾Á½À¿¿¿¼¼½¼¾¼½¼¾Àcc\`^Y[XW[YTPRPPRSNKFF=?=;BN\l}„Œ“–˜¤¦©¬®³°³¶¸²¹¸¸¶¹·µ¶®¨¥œ˜’‰wga\OMUVUZY^d^hgkisnpqorlls„uzxpmltsgODYbw|yw]M93833*4/<_\RG^wwng€dI45EjuZF&$#&/5-3NShqRB8*4gŒ”|SB;CgSz¦¡Y,/)-.3300341&+,-),15165245*.,O¢~gen޹£±¶¼»½ÀÄÈÍ̤@?GQ\_lptx{|}†‰ŒŽ•–˜˜˜Ÿ Ÿ¨©¨£¥¢ª¬¬²«©¨£¦ª¤¦¨®±°®¬¥©¦¦¦§§¦¢§œŸ•ˆ‰‹Ž’І‡ƒƒ…‚„ˆ˜Ÿ§¸ÂÍÖÚßÙϽ¶µ²­©£Ÿš™–˜“ދЇ„ƒ€|z{xzsokhfZC=;96+5/29C<XuƒŠ‡…„†ƒ‰•›Ÿ œ™Ÿ›Ÿ™šœš—•–—–—”—”–˜˜šš˜›–™”•‘’“–•›•—“—’“‘•‘“”Ž‹Œ‘’ŒŽŒˆ‡…‡„‚„„›¨®µ¹¼¿ÂÄÅÅÄÄÃÅÀÀ¿½¿¿½¼½½½¾À¾¿»¾¾¿aa^_^]X[]XUUWTOTUPQPEBD=?>IVZgy†‰’ššŸ¨§©­¯²²±µ¶´¸¸³¶·¶°­¨¢¢—ŠuiaWOMRSX[cad`fhfjnoilnpsnquzzo`ns‚yoeq…”‘„w\?889201/7OmaMPh‚t_uj_?AGllP/&$&'02.9ObriWP=/,--63./,-)01,417,+2,)RŠ¥}hgpŒÀ²«µ¼¾½ÂÆÉÉ»Z67EQRakpqoy~yƒŠ‹‹’—š¡›ŸŸ¢ª©¤¥¥ª­©±­­¬«­ª¬¬«ª¬­¨©¨©©°°­¨­¡œšš•”ˆŽŽ–‰‹‰€€€z†z~‡Ž˜¥¯ÄËÑÛàÜÒÆ¼¸µ±¯¨§ ›š˜•‰„ˆ‡ƒ‚…~|z{zrqne]JA/84(-8?A1??RC1,*:| ¨°·¥u97LJ*%1A910*,431../-CFFB9?`zXGRnyu{X\p_hys]J*'$))%)2-14:D[muTbnlbXZN\• Ÿ˜vECjb_RQUMB'%(434*'/-*)/--.+)(-Bwœ–m`jv¤¾§±¼¿»½ÆÌÎÁ{64=GKVgmrpux|{ƒ€„‡Œ”˜œ–›Ÿ¡¥¥©¨¬§­­¬°­®­°²±­®¬ª¬­¨¤­±±²®²¬¨¢œœ˜•“ŠŽ”‹ˆ„†€‚†|€zƒ‹œ£±¾ÈÓÝàßÒÊ¿·°°­®«£›””‹ˆˆ‹ˆ„ƒ~}y|vtnl]NG8<6325;71F8Y>(#&5yŸ¢¬³¦•wF4G<)%+B3+*,2891+-.+;[p‚Œˆ‡…††“ž œž›šš˜šœšœ™˜›šš••™šœ—•˜™˜š™—”˜‘’“•’““““’Ž‘‹ŠŠŠ‡‰‡†‚‹Ž˜¢­·¸¾¿ÂÇÈÆÄÆÆÄÄÃÃÁ¿Â¾¿¿¼¾½½¼¿Â¾¿½¿Á¿^^^]kd]YW\UVVTWOMLNLKBC><3IejTI\iyqSSbyr€vcE)(%)*'!+823.+5KTjfqvz}pk_VhЇi@1_}€vfSVYS/'.36-,10,050+*,)'(-T“¢|ffpˆ»´®¹¼Â¿ÂËÎÉ’423?LS__oruyxzzƒƒ„ˆŠ‹’—™•š ¡¢§©¨®§§±¬´°­¨±¶µ¯°¯¯¬«¥©®²®²«®ª©£¡œ˜––ŽŽ‘Œ‹†~‡‡|€€„–œ¬½Æ×ØáÞÓÌÀ¸¬­­¬­©ž™••‘‹Šˆ‹…‡……}‚~{|vxrk\G99;0,18A75I;S70+'/t ž±®žœyJ2A5""(C=-#09585/./)Dev„ŽŒ‰…ƒ†‰š ŸŸŸ›š›››š•š™˜–˜›™—˜–•–•˜š—š––˜™–“•“”›–’““’‘”•Ž‘ŽŽ‹Šˆˆ‹Žˆ††‡‡ˆ†ˆœ¤©²¸¾¿ÂÆÆÈÅÅÆÈÇÅÁÁÁÁþ¿¿½¾½¾¿ÀÀÁ¾ÀÂÂÃZZ\]gdVXSQTOSSSLJKIEFA?;9:DNbl{…‹’—Ÿ §©®®®±²´²³²¶¶·¶³¸±­¤ ™Œ‰„tk^TONOUY[ZZ^cbfggks„”¡£¡™”–Ž™¢…M>?BFF:QmgVLVwrOQbu€ycV?/,)2),')(.*)/0>TPCGou||vjM;51.2_††‚rca_J0-354/:-/,00-+0)))&1ož“nfnv¦À«´½¼ÄÂÉÐ̨>+*6AKTZfqutuz}}}†ŠŒ’”˜˜•žž£Ÿ¥¨®¬­©­¯¶µ®¨ª«²²±¯¶³±°°­®¯±°°©«¥£Ÿš“‘••”‘“ŒŒ……„…†}ƒ‚…‹“ž¨»È×ÚáÝÔ͸­­©§¤¦¢ŸŸ—”Šˆ…ƒ†‚‚}‚zuvniXF:8=4-3@I6:E=U9+3+,g¤´«¤ŒQ/D@/.*;9,(-393+./-4Qiz‹ˆ‡‹‚…’ššž Ÿ›š›œ—–™šžš—•˜™•—–•——”–••›˜——–‘‘—Ž•’‘‘——‘‘Žˆ‹ŠˆŒˆ‰„Žƒ„†‰˜¤ª³¸½¿ÀÂÅÆÆÄÃÄÃÂÂÃÂÃÀÀÀÀ¾À¿½¾¾¿Á¿ÁÀÁÁÃWWVXZXWUVSPULMSOMNKHJ??;58AMat{~‰’“œ£¡¬«®±²°²µ¶´¶¶³¶´´³®¥¢ž•މ€uh[PJNONRSYY[ahkhuƒ–ž™‰‡’Ž€{Š€~pC05.655/-.../-/1.-./10+,6S‰ª€spt}µµ³¼ÀÃÃÆÏϽX)%/4DMW_iowuwy|}~„‚ƒ‰”“—˜—˜šž¢¢¥¨©«®­«®³²´°¬ª´³°±±²±°­®²¯²¯­ª©¨¥›œ”“””˜•ޒІ‚ˆ…ƒ„ƒ‚…‰‘ž§ºÊÓ×ßÚÔÏĸ¯¨¦¥¢¡¡ž™—“‘‰ˆ‡††€‚„‚}w~qh_K9?<0+<;B>5CCU8,+)/_”§´§—¦Žc:C6)**42,2689+0)+.4WoŽˆŽˆˆ‡—™ ž¢¡žŸ›ž™˜œššš›—–“–”™—™–œ•’•”›˜••••“’’‘’”“‘‘‘’Œ’‘‹Šˆ†‡„…‚ƒ„‡¢¥¯¸ºÀÀÂÄÅÅÄÃÄÅÅÆÃÂÂÃÁ¿ÀÀ¿º¾¿ÁÃÃÂÄÂÅÆÅYYTSY_XTQPJNQ^MLMOHGIC?9;@?A=9I=4?HVdXFS]wwcoŒ†~u:5505640*/13.5,(0-24G^XA6??SjM/$$(12Da€ŒtgF78)./2..--./*/3))*0:l”—rsvt¼¨¶ÁÁÃÃÍÒÉ|-!(.8GU`blow|{z~€‡ŠˆŽ””‘“——ž¥¦§§ª®­¬°±³°µ¯°´±¶´®®¯´³°³²²­ª¨¦¢¡š™š’’””ŠŠ€…†…ƒ‚‚{‰ ¦»ËÑ×ÝÛ×ÐÇ·±«§£¥£¢¢—˜”ˆŠ†‡†„~ƒ‚~€x{zujZF9@A1-/B<64OAW93++1]–¢­¥”¥šk2:2,*.,6))2303**,+BZuƒˆŒŒ‹‡ƒ‹”™ž¡¡žž›žœ™œ˜œŸ™˜—–›˜—™—–™––›”–•”“—›•‘“’‘‘•”ŽŽ’’’‹Œ‹…‡‹„‚ƒ„ˆŒœ¨²·º¼ÂÁÂÃÄÄÅÄÃÄÄÆÂÂÃÅÁÁÁÀ¿À¿¾ÂÀÁÄÃÇÄÊÉÉTTUXSZWQOOQNOUOMJGGDCH?<79?N]kz”—›¢¦ª¬­±´°±µ³¯´¶¹¸´´­©§¢œš…udYWORPRWZ\^`iky„ƒxtuqjv€|w‚€s\_WLEKOJNYK:;=A96?:HQSnV:>KZky‚Œ“•œ¤¨«°°²¯°³³²³¶»µ±¸¯°°§¤–‡~tl_ZOINSWVV_`dlkwnmopsru€ƒ…yhifj]i`_VUTZ[\>:BD5/:=CLbop=7KRs~|‘vNH+A^@-&'"$/^bkŒ}kZf_70+#)+/,''230&!*,DXl{ƒŠ‘›¢¥©­¯±°³±³²´µµ³³´±±¬§¢˜”’‰}ogaXJKPR[[[]aiinfelmopqp|ƒy\Z]NH[bYUYh_JO:)0>445:G^L5,*'.L†¢¬¥|’¨’T(/#&,*0K6571.1.+8Wn‚Š“ŽŒ†‡˜¡ ¡£¢ ™››˜—™™šœ›–˜˜“—–“˜’˜’–––’“’‘’•”•“•”Ž”•š“‘ˆ‡…‡‰€€„‚’ž§³µº¿ÂÄÀÂÁÁÂÂÂÂÁÂÁÃÄÂÁÁÂÄÁÁÀ¿ÃÆÆÅÈËÉÉÉËËËTTQMPRRMNKMGKMPHHMJEDBK88?EHYkz‡—› ¦©©®²±¯²´³·µ³´¶µ°°«§¢˜“‘‰†wg^WMOMZYZ_]bcbnifonlnwt|„}nN][HHRUK^fcM@Z]OK429;<7>G]jy„ˆ’•˜£¦¨ª¬¯­³³³²±µ°¯´´²°®¥Ÿš—–„€oh\XNKTOWTZbca`jjnolrorut}~jQQXTZPJfwq\B_~eYC<;GXmuz]<18'%$','$"&2%"")[‰”voos„¿Ç»½Æ½½Ãά:/+-6.37@OYelrquu{{‚‚~‰…ˆ†„‹‘“•—šž¢§©¥¨«¬°²´¶³°®³·³­¯®©¯°¯«­©¢£Ÿœ™–‘“’”Œ‘ˆˆ…†~~|‚„……‰—§®ºÒ×ÛÝÛØÍ·°©¥¤¦¡¢›™˜–‘ŽŽŠ……‰€ƒ€~zriaE=;71(19845:H`K2*)',HŒ¦ª£…˜¥˜a,.$+;)3<>33/-,*/IayƒŒ‹Š‡‹—™¡¡¢ œšœž—œ•œ˜šš™˜œ˜š––’”–“—•’˜”’‘””–Ž‘•–’‘’“‹‹ŠŠŠ‰…ƒ†zƒ“Ÿ¨²»»ÂÅÆÅÆÄÂÁÁÀÂÁÁÃÄÄÂÁÃÂÂÂÄÂÆÇÉÇÉÊÌÎÌÍÍÎÊÌSSTRLKGSHJJHKHHFI@AG@?=985CLXg}„Š’–ž¢¢«®«®²°¯µ°¯´®°±±¯®«¤¥ž™‰„ui^RKIMQVXYY^jjheimmlqrsqutf@@Zwgh|‚|^HG_j_JGJ\lwkO;/+5;VQg›‚]YmkB>M:C;+)+,4>336371.3123--I*.-'1@[<(""1KYepƒmm•¦“wX-%# #$%$$""!%9rš‹srps›Æ¹»¿ÄÃÅÌÂj20*-:/853-4211>95,:A/00JD)%":A`fq{gZ°šzE*"+/*#!")&"$*S…Ÿxstwz¶Å¸½ÄÅÉÐÌ—2)))5D,1<=KU_entyvxy}‚‚‹„„…‰‰‘Ž•–˜™œ“››š¢§£§©ª²¯³¯°¯°·±®®©«°®¥¦¥¤žž “’ŒŽ‰‹Œ‘‹Š‰‰‚{u„€ƒˆ–Ÿ¨ºÃÐÜàÞÙÓ»ª§£ª¤œž™˜–”މЇ„ƒ‚‚wzreTD>:4)'=12//3E\I-,)%+@y­´¯‘ˆžž}<+)"$#1CC@-1835=Xu„‰Œ†Š¯£Æ¥¡¡›ššžš›šžš—šš•””–•••“””’“‘’Ž‘ŽŠ’“Š‘‹ŒŒ‡ƒ†‚„ˆ“ ®³¿½ÁÄÃÄÃÃÃÁÂÂÁÀÁÁÁÀÃÃÂÄÃÅÇÈÊÈÉÌÍÎÎÍÎÎÊËÍÍÍIINKLMGLIKLPKKGGECEEB>:78=CFUgv“”›¥§©¬«¯¯°±­®°°³°¯±®ª¬¡ž—‰…vf\PIMZRY`Yb`]behfkklnsrrrhRU—”“™vZFCTmjpb?F]s…lL:99@8C283Gr{^QzL2<04=COQchlpzz{{{~€ƒˆ‚‚†ŒŠ’˜•š™œ—š ¥¥¥­§®±°¬¯³±°¯­«©«ª¬ª§§¥¡¡œ’““ŒˆŠŠˆˆ…ƒ€z}y‚‚ƒ‰š¦±ºÐ×ßßÛÔ¿¬¦¦¤¢Ÿœ›–—“”™Šˆ††|~yzmbO>?1,)07/3159K_L0&&($5w¨°¬“ˆ™›ŽH(1()$-A=:604.2Fax‡ˆ‹‰†…¥­³²ž›œœžž™—Ÿžš˜˜š——™™™•“”””–’•”–“””•““’ŒŽ“‘’‘ŽŽŒ‹†‡†€‡‚ƒ›¨±¸¾¿ÃÄÄÅÀÂÂÂÁÁÀÁÁÀÅÁÂÃÃÄÃÇÆÊËËÎÎÍÎÌÌÎÍÍÍÌÊÊNNLKIMMHQLJJLKKED@CD=96524?IUjy„“•Ÿ¡¥¬¨¬¯®®¬¬®¯³²®±¯¯¨¨Ÿ™˜ŽŠ‚te]RLIINYXhrob`ecfjioknoopvu‚£œˆr`AKFPp}ijF;^|^;:79IN8R533@^‰~kM€b828F<:4870E63/-/2.)/1/),-Hk_1%$/E5*.(2Mpo{rVJu­¤š”pI-&!"%($*+*M€—ydp{}­Ë¼ÃÈÌÐÕÈu5)"&'?D207=GQdhlpwyy€€€‚„‰Œ‹“’˜‘™˜˜™œžŸ¢¦©¥­¨«³®®­±¯±°²ª¨«­¨¨¨¤£Ÿœ—˜•’‹Œ‹‰‰‹‹†„~}}|ƒ„‰‘“š¡«¸ÏØÞߨÒê ¤¡¡¤¡–™“•–‰ŒŠƒ‚ƒ~xwn`Q>?*(03:.59>>LWF1'&(%9s¢¯¦˜Œš–ŽW8=#!&47?:/6215Kf~‰‰ˆŠ‡ƒŽ”ž£¡¦¡žšœ›˜œ—›˜š˜š–˜™›—“–”—•’’“”“’“’‘‘’ŒŠ‹‡…†‚€ˆ˜¥°µ½¿ÂÃÃÅÄÄÁÀÂÁÀ¾¿¿ÀÃÁÂÃÃÄÈÉÊÊÌÎÌÍÎÍÌÏÍÌÌÊÊËÊJJLPRNMKLKOMNRHFJGIC;@<663:GXhw‘›Ÿ£¬¬­±°«°©ª­®®­±³±©¦¥––‘‰ƒtlYOPDOTPZs„‚d_dbjlgkgnzƒ‰–š•ŒtTH/@JXp†€jP@VƒˆX<467VI>7Mr‚tX„€E(3A:B:@53I94-27+,)+*3('/LIU2(.3=9---=`nv†ƒhOO‰y“²˜n2!/"&,+('0`•mdpy…ÃÉÅÈËÎÑÔ§/"%%(/9H88=GQS^enmwtz€~‚‚~ƒ‚†‡Š—”˜–˜˜œ›œ¥©§­««´¯­¯°®²¯¬¨§¬¬§©§¤¡¢ž›™”‘Љ…ˆ†…‡ˆ‘†ƒ~€z}||„‰ˆŽ‘–£«»ËØÜßÚÖȪ¡¡¢žžœšš•’•‘‰Ž‹‰†„†€vwk\G<6010753>89AO\G*!$"$1lž©§œ…˜“e4D$1(.;4<,-(*>5375=HXkx’“›¢¥©«°±¯®®«®°²±²±°©¨¢žš”’ŠviUUFHJMP\y‰h_a`iigr{Š› ¥žbD:;61FSnŽ”zW?\}`9.,.5`uHHW68:sŠSl’Y=87874<90@C1*).'(3+*:*''?GDAGF>92*597:L`O/(%%&3g˜¤¥š–‹h=9 #-8?5871,,=^yƒŒ‰Œˆ‰™›£Ÿ ¢¡œ›ž›š›œ™šš“š“—•”—’•“’”’“““““”Œ“‰‘‘Š‹Œ‡‹‡‚‚€‹š¤®¸¼ÀÃÅÂÃÂÂÃÂÀÁÃÂÁÁÁÁÁÄÂÇÆÉÉÌÎÌÐÐÒÑÐÐÌÍÌÊËËÉÊËTTNQHOLKKJQIHKJMEA@B;92410AJ]lwŒŽ”ž¤¦¦«®¬ª­¬ª«¯²³°µ²°¤£ ™—vn^PKMMKL_{‡t]Z^]ijw¤©©™‘ŠoF*.7:5QkœœvIQ{{[183/+;tnIMV76W—‰ƒŒ…\Z’p2546:7R>7C7,+%.&'*%)0(*+ASMLCGM:.8HN[\xœ¡˜‰a=5St›­˜i&!%# &[Œ”|nkt¶ÎÆËÏÍÍÉ‹1"''$+&DU8179LVZefoqsv~x‚…‚………†‡‹‹“•˜˜š˜  ¤œ £¨¤­±©ª§¬¨®°«ª¬¬¥§ ¥¦ŸŸ™™•‘Š…†††„ˆ†‚…€€zzx{|‚‚ƒƒŒ›¦®¹Ë×ÝÞÝÛе££¥ ›–“““ŠŒ„…†„‚zwdPC70)(-776D95?KWW0$&$,,b‘¡¦›}–“ŠoL6)$/88951.'0Nf{‡‹Š‰‹ˆ‹–¤¤ž£ ¢¡œ™ž›ž˜˜š™šš™›™˜”–•”–“‘‘–‘“‘‘’‹‰ŒŽˆ…„ƒ‚ƒ…¡«¶¼ÁÂÃÇÂÄÄ¿À¿ÁÂÂÀÁÁÄÄÆÇÉÊÎÌÍÐÑÑÐÒÏÏÏÎËÈÉÈÇÇËPPJGLNHROPMJIGFCFD@GA:7113AK_ix€‰•š¡££ª®¬²¬«®®¬°¸²±²°¨¨£¢™”ކseYKJGOEQ[{`a]dt‡›¤¢”…wxeA',91@o|Šš…b`yxH=2<<-4Q{dDi[2:b™Œu~‰`D{†K33<62;>.,&&)'(%*0,1>R[[PMUO2-HbWPc‰¤§© uH3Cb~“`&$$;œunp~ ÆÇÊÌÌÎÌ¥?&###!!+AW<75F:=ALVX-%)(/2^‹¢¬›{žšŒuQ. ,:742,-(@Wl~‹’ŒŒ‰…Œ™£¢  £ Ÿšœ™›™™—™——•”˜“–•˜““•“–“’”ŽŽ‘‘ŒŒ‹ŠŽ‘ŠŠˆŠ‡€‚Žœ¥±¹¼ÀÁÄÃÄÄ¿ÀÀÁ¾ÀÀ¿ÃÅÈÈÉËÌÎÎÏÐÓÑÒÒÏÎÏÌÌÊÉËÊÊËSSLPMNNLNOOKMPLEA>E>:83/2,:NVex‚Š“›ž¤¨«¬­­¬¬¬¬«¬¯°²­±¬§¥ž“Ž‹wcXNJIIHMVs”Žo\ex‘¤¤˜oq|ˆ€X1*76=`~€‹ƒSRzyH;>8>917cp]N’jAMy‘zdsrAn—b8B<5763=FRex~Š–”˜ ¢¦¬©¯¯­­ª­«­¯°²±±«§¢¡˜–Š€seWPFLPIKVd›Šol‰¡…vjpƒ‹„qU63?HlŒ|}~]29=81;70)"'*%%$)6-;Do‚{fVC;30NWf{ƒŒ»··µ”^)3=\…vF$#7p ‹m~vu‘¾ÃÉÊËÏÃd(!#%&!'.KVD5DNCNX-)!"3I‚œ¦¦‘Œ§‰T+ *80(,,,2IewŒŒ‰‹ˆˆ™ž ž Ÿ žœ›™˜˜™šš™ššœ–™—–—“˜”•“””“‘’•”“’‘’“’’’ŠŒŒŽŒ‹…†‡ƒ…ާ±»¾ÂÂÄÅÄÂÂÁ¿ÀÂÁÂÁÂÁÄÄÇÉËÌÍÎÎÎÐÑÒÑÑÑÑÒÍÎÎÌËËËÊÌÎOOOLLOKIIPHGJJGEGA=?;:82-/7BUg}Š’—¡¢©©¨ª±¬­­«­¨°°¯²­¬«¤¡Ÿ”Œ‰~ubYRGEGJLL\l’œ††‹wnsn|…{uY<:GOz•wqiCNk[PgczA26Tk\KMy‚L:X‡wwcv–†X[ŠJ88).:74790(%#))%(=H4:>^roO>8:AFfv{xv´¼¹¶™h,&2LsM& !%Pƒ•yoyvx¥ÇÈÎÎÍ̉1%&%'!#)')I\H6=EIMZbflrtu‚ƒ|zƒ†~„‡‰ŠŠ†‰š’‘——›™—¡¡¡£ žŸ ¢£¢¤¨¤£¨©¨¤¥¨¢¦¦Ÿ ›—‘‰Šˆƒƒƒ‚„„{{}xwxz{|}‰Œ•Ÿ¢®¹ÂÔáæãÜΨ™¡ ™›–𔑑‘‹‹Œˆ‚„ƒ}vlgXE2((+,90,;CDAMHDX3$$"#5H{𤧖…§Š‡X,%-;+).0*0Qd~‹‹‰ˆŽšš¡ŸžžŸž ˜““™››œœ™››™œ—–›™˜—”“””’•””“’’“’“‹ŽŒŒ‰‹ŽŽ‹……„‚„†”Ÿ®·ºÁÂÄÄÂÃÁÂÀÀÂÂÂÁÂÂÃÃÆÈÌÍÏÏÏÎÏÐÐÐÐÐÐÐÐÑÌÎÌÍÎÌÊÌÐPPQNOQJIJKKIFGGCD?A>6761,)5BZey‚ƒŒ”œ¡¤©®¬®¬­°®«®®¯±±³®®©§£™—Œ€rfYPIKRIIOW]n…Šƒuneikkr|uvdH7C=n„lqQAiaZHK`V5?H`b[QqhK:c…ogly¡•\D]‰I&-))89@7;/-&),*%(080E<@TkjO9;MqenuzgMм½¸žy<#.=N5#  /k†sptv¿ÍËÍÌΣ8%$&)%&'"$%MfO;CGEKWaglqqy~€z~€ƒ‚‡ˆ‰‰ŠŽŽ”“’˜›—–™šž¢žœ ŸŸŸ¢¤¨¢¦¤¥¢ ¤¥¢¦Ÿžš””ŽŽ……„„€„}„‚{{zy{y|}‚†Œ’ «µÄÔâåâÛÒ¯”¡ –š™—”’–Šˆ…ˆƒ‚~ytkcUD.,*0742.?A?=NDQT-&"&4Lq™§§š€¦“_-"$77,)4.59Xr€‹ŒŒ‹†Œ—›£¡ žŸ ›Ÿœ™““œ—™š˜›—”š•—”•—›—˜—“’”•–—’““‹”‘‹‹ŠŠŒˆ‡Š‰……›¨²¸ºÃÃÆÄÂÄÂÂÂÀÂÂÃÁÁÃÃÃÆÊÌÎÑÏÑÏÏÏÐÐÑÎÏÒÐÏÏÍÑÎÌÍÍÎÏQQVPWNKFMJEEKGLICCC5722/102>Shw}†‘—›Ÿ¥§¬ª¬®®ª²²¯°®±¯²±¯©¤Ÿž—Š~rhWPJKFFOMZ\hoqx~vwztpr}„‚~fI454cvniOLcTWS^J29VYYXM]…xdT=[zcZp{š_:D{S!*(*-'109/+.(,#")6/6ZIGPX_SJILP3'"!"1Ln—§ª˜¥ˆm,&#/0'*(.;<_q‹ŒŠŒ‰Š“ž¡žŸ›Ÿœ›œš”™œ˜”™™š˜™˜™›•–––––”–•••—–“‘’‘”ŠŠ†‰ƒ„…Œ”¦°·»¾ÃÅÄÃÄÁÃÁÀ¿ÂÃÂÁÂÄÇÅËÊÍÑÑÑÑÑÎÎÐÐÏÍÎÐÏÐÏÏÎÏÎÍÐÎÎOOPNTNMMKNFIJGKCDEA73443*+/DPgw€„‘—›¡¢£«¨¬°°°¯±±°±±²¯®¯©¥¢ž—”‡~reVQIKPMPOSZ[]_dkv|{‡Š‰ˆƒˆ{XG48Hnhg]Iah^Xi^98OlcSIFvT]_D^|\Op–šj:5vn%$&*.-.0:31+1.-*9223g^hLK[JG[z£‹punYA;=ZOBD.*$):2!#0#,(\‰Šuxxy’¿ÌÎÊ¿Å~/""'+)"(''(0VdZA9HESW\`fquww}€€†„„„††Š†Œ†’—™“—•˜”–š›œœ˜ž¡¢ ¥¤¤ ¤¤£¤¡ž›””’ˆ…}|z{|{‚ƒŒ…|{|~yuz~ƒ…‡’™Ÿ§±¹ÑàæåÞÒ¼•œœš˜•–—“ŽŠŠŠˆ„€‚}mdVF0(+)0-52BEBIEGHEI56#! 2F\Œ¤¥›„ ‘‰s67--.(&-,LDc|ˆŽŽ‹‰Šˆ“ž ¢ž¡™žœž™œ™•›˜—˜—˜˜”—œœŸ•¤–—“”‘”•““””•—”’–“’ŽŽ‰ŠŠŠ‡†‚€†ž«³»ÂÃÇÅÄÄÄÂÁ¿¿ÃÃÁÁÄÄÊËÍÎÑÏÑÑÑÑÐÍÍÏÐÍÏÎÏÏÏÑÑÏÑÌÎÎÍTTQQPJJMLJMHOINCB>?<9503/%2FWes~†•›¡¤¨¨¯¨®°±°¯®­¯±²µ¯®ª¤¡Ÿ™’Œvf^UMKIKSSUZ\]gl|‚kku…„€|uX@:>H_VSMWdl`_fB9X`bUJ@Kz…FFN>\qTFhˆ“£l?2b‚6$'-/.3++,51/930//*1SgI?EOGRc…vacYC2DX_G;2*%1,7/#.OKm‡vr€{~žÉÊÊ«Ÿ¢<'"%'+'&,%%,Yk^=CGMRYXejmutv{}|€………Œ‡‚…Љ”’˜–”™–——˜–”›šžŸ¢Ÿ Ÿ¡¥¥ž£¥¢Ÿ›˜’•ŽŠƒ|wry}‚††‹‡ƒ€~{|z|{~„‡‡“˜¢©±´ÎàæåßÓÁ‘˜—˜—˜–™š‘Œˆˆ†„€~~ngQ7*(*+/,3,-:CADRAADD.(#"/EY‡£œˆœ—D,)/+'#$/ENh€ŠˆŒ—›¢ž ›žœœ›——›—˜•—˜–˜”–™—š———•”–‘”“’•–”‘–”‘‘‘’Ž‘‘ŒŽŒ‰…‚‡ƒ„Š—¡¬µ¾ÁÆÅÄÄÃÃÄÃÀÀÄÃÂÂÄÇÈÊÌÎÑÑÒÑÑÑÐÏÎÌÎÌÎÍÏÐÏÎÏÐÐÐÏÐÐÎPPOOPMOSPKPNRJJM@>@873.5+'0>Nax„Ž“œ ¤©ª¬®°¬¯°¯°´±²¶²´±««¤œ”‹~vnZOHEIKMQW_`iu„xkhmrz{|wh:;LVc\Z=Detse\@<0,%(#*,:b4.SOO`y¨wcQHAFn‡sO:/(##0/+#)Qfi~…jnrr¹Éæ‚–|4$##)%"*%)*\l\A9GHOV]ghlsuuy}ƒ‚ƒ‚‡…ˆ†‡Œ‹’““•š›™˜˜—™š–šž¢¢Ÿ¢¦ž¢¤¤£¡œ˜˜˜‘’…|yurv|„…ƒ‡†„ƒ‚„y{{††Ž–¤¬³¸ÌÞææàÔÆ–“Ž‘—–—•‘Žˆ†…€~~yqsdJ1-,(.-57.0;DCOSBIA8-""$3;T…ž©›˜¡€_75)'#(#-JYt†“ŽŽ—¤£¡ŸžŸ›œ›š›™œ™–—˜—˜–“”‘“••—••™š”—–’“”•“–‘–“’–‹ŒˆŽˆ……ƒƒ’›¨µ¿ÀÄÆÆÆÃÂÂÄÃÁÄÂÀÁÁÆÆËÌÌÏÑÒÏÏÐÏÏÏÍÌÊÌÎÏÏÍÎÐÐÐÎÎÐÐÐÐRRNLMNOOOMQIKNEIA:;6650++%2=Tavzƒ–™ ¥¨ª®¬®±«°¯®¯¯±³´´°¬§¥ž˜…}vi\NIDBJSVYg}}zljmknstrnU6CGUVXV2GkxbL?W[OFD@?G~Y33=NrWY[l…ŸXFT^m–¢”€_K„“­ŸŸ–•‹_3<7%""'/Xq~Œ–‹”‰”œ  šž™šš˜š—›™˜”‘™–“™›–••”˜™™—•–™˜•”š’‘•“–”•‘•¢‘”’‰ŠŽ…†‚€ƒ‚Š›¦°¹ÁÁÅÈÄÅ¿ÃÃÃÁÁÄÇÈËËÍÏÐÐÏÐÏÎÍÎÍÌËÌÌÌÌÎÏÏÏÐÑÑÐÎÍÎÏVVUUQTPPOPPLKJG@A??965/.,(.APfy†Ž–› ¥§«««¬®°°°°¯²³±³²²¬©¤š“‹~zeXOKBJS_p…‹‰ƒijcknkkquwnhODJ=?\^HRxt‚nBP]]IARI/8x]M2&7Tod>I”šU20PˆF+009HL64.+&79$ #59BPh¡“Ÿi@5@PYgqe?2*-)'!'$&1' !*xª¡¦¤™–’€uw‚¡«½±e1$$$(&2'#'(%,]qc[EEJL\Zimotx{€x|‚€…ƒˆ‡‰Œ‹’’‘“•——–™•˜¡›ŸœžŸŸŸ¢ŸžŸ¡žš•’’ŒŒ~xhpv}††Ž‰ŒŒŒŽŽŠ‚|y|~‚‹“¬´½ÈÔÞâÜÔ¿‘Ž“–•’“‰‹ˆ†€‚z|zr[?.%*)(&0-308?<@JHIL;/8*""3)7v”­£ –‘’ˆlA22#"'$.[u~Ž‘“‘‰’¢  œž›ž£™˜˜•™–™•’™••”•–•—’”””•“˜”’‘””•‘“’‘‘ŽŽˆŠ†ˆ‚‚„…‘¡¦³¼ÂÃÄÆÅÅÅÃÁÃÃÃÿÃÄÆÉÌÎÏÏÑÏÏÑÎÍÏÍÍÍËÎÏÌÎÑÏÐÐÐÎÐÑÏÏÍÎTTXVUOPOQIJLKIHKIG?A=;,1)(4ETiw~…‘“𤦧°°®¯®¯¯±¯±´±³³³®©© —ˆtkZTMV`nŠ„te`iikhsvxusq]EOGH;[ZTnyuwULZOD=GCR0F‚ƒQK.,2MtŒdH@f‘š•`25;f.2>IFD1/(-:I=#%))0-,++:NXiu„–š¡§§««­«­®­¬®±¯±³µµ¯®±£œ‡wqb[Ymy~ng^^bdeglquxzwjYGM>‹ŒB0+8=7+0+2;PC* !(+7J88//,((*2?i‚rpF-%$$'".* b¨žª©§¯©¬§±µµ¸½­ŒwN5(#&&',)11/&'2Xi\V?:GUZ`entsv}|~‚}„€ƒ€…†ƒ‹Ž‰Ž—Ž˜–™“—•——”›œ›Ÿš—œ¡ŸŸžžœ›š›–‘“‘Œ”€tlt|}‡Š‡†‡†ƒ…‡„‹„~wwvz…‘” ®ºÄËÎÔÒ˧“”‘”‘‘ŒˆŠ€€~~xoYD1)*+,))69325@=HR=FO<,10)&4(*g¥¤¨ ”yrP2.+%%%>]‘‘“’Ž”ž¡žŸœž›ššš™›™™šš—”™—˜˜––”–™—•”–˜••‘•–˜“”–“’”“ŠŠ‹‹†Š…€ƒƒ¡©±ºÀÃÇÃÂÄÂÃÃÀÁÂÀÄÃÉÉËÍÊÍÎÎÍÏÏÑÍÎÌÎÍÏÍÍÌÍÏÏÏÑÒÑÐÒÑÐÐÐÎZZQOSMP\RLRRGHMIBDA<>?;1-,:KXe{†•—Ÿ¤ª¨­¬®«¯®«¬®­´¯²²°¬©¤™›ˆ€}nbei`gd\]Y]^^aedekox}v{dYTK25J]cibhzkjXLAK/$3Ql…ZS=B.*6aoSIUvŸ­„C75~¦_0(-2,.0.3:_H% '++:LC?9+(%*3CKqbOF3('''&&,) !%Œ®¥¨¨´´©°°´¹´¶´™{L-)& """0-+&()%4`od\HMLU\]fmtoyz}~|}„}ƒƒ‚ˆ†ŒŠ‘Ž’••–—•š˜•—•—™œ›Ÿ˜žžž Ÿ ›ž—–”Š‹„qlq‰‡……ƒwnt~€‚„~zzyy„•§³ÁÉÍÌͺ™’—’——’“Љ‹„‚|yvhI1,**-,.-05236;:HG:=K=(/6*#1*,WŠ¢©©¡Œ…vV)$/"!)Be”–Œ•  ¡ž›˜—˜˜š˜—””•š•—›˜—•š™——™”––’’‘”—•““•—“Ž’ŒŒŽ‰„„…‚„“¢­µ¼¾ÂÆÆÅÄÂÂÀÂÀÃÃÆÅÉÉÌËËÍÎÍÌÍÎÏÎÍÍÏÐÏÏÍÏÐÑÎÏÐÓÑÐÏÏÏÎÎÍWW[PPOTLQONJMPNED?BB;98632CAML7@N:0/40$*(&X›«§ ŒŒ|`1(3(,/Um‹‘“’—ž¡ žœš˜š››˜œ˜””“”——––——–˜™•–”•Ž“““š’“‘‘‘’ЋމЇƒ}ˆ˜¦®¶¾ÁÃÆÄÅÄÄÁÃÅÂÄÅÆÉÈÍÎÍÍÎÏÍËÏÍÍÍÎÍÏÑÏÎÏÏÑÑÒÐÑÐÒÑÐÑÏÐÏÍMMSPQNNSQIPHHMJFDBC;DA9:05ATbo}ƒ‹’–˜ž¤¦ª©©­±¯®±­°±°´³±±°«¥œ—Œƒ}qiaRDOLOTV]Z`bjihkkotrj]NOTK@UeYQfz’}cVPN5%$6g¥‡]X;@^>+9Rym^cXl”¬©k4.Q~;(.116HMB=O=%##(!!!)/;OB05g°ª~4#%"&+*.,,&%"!X­±­µºÄ´·½¼¾·®¢t0)*#!%%%%&,8.*.++-Wjc`LIQ`^`bkpquyxyxy€‚€‚‚†Š‰ˆˆ‹Šš––“™™–›œ˜•š›š˜œ ŸžŸŸž Ÿ¡˜“•“Š‚wy„†ˆƒeA201?KY`Zcquz~y{ŽŸ©¹ÀÈÇ­Ÿž–•–”˜•“‘ŽŒ‹…‚€{vjK1+(.2.-)09654988>DWgu†Ž–˜š £¥¨ª­ª­¬¬¯¯¯°³²¶³­©¨¦Ÿ–‘ŠqeYJKINNW\\[]aggjgpnqokO{”­­ž}˜`6#(@1BdzŽ””““”›žœœœ™™š•›–˜—“˜—–••“˜–’˜—•–––“–’–“”‘’‘’’’’“Ž‹ŠŠ…„…‡‡“œ¨¶¼¿ÂÇÆÄÆÄÅÄÃÅÆÅÆÉÊÍÍÍÎÍÏÏÎÌÎÎÏÏÐÏÏÒÒÑÑÐÑÑÒÑÓÑÓÔÒÏÐÎÎÍNNUJPIPMKQJOQNIJGAD>@GF==@NZhx}‹’•™žž¦¨¨­«°®¯­¯±¬°°²³¯°­«§ —‘‹„|oaTLHDNSQSY[b`cbgipmookTIdaO=]^^Tn~{fYNH+"'/g”hA5GFS:6ES>0.-8<%$!;u—ª¬Ÿƒ}›…^=*'7HJp‘”•‘“Ž‘š¢›Ÿœ››š—š•™—”——˜•–•“—””—š˜—••’’•“•‘‘‘’•’”Œ‘ŒŠ†ˆ†„†”¢«¹½ÁÄÄÄÅÄÃÃÆÃÅÅÇÇËÍÎÐÎÎÏÎÐÍÏÍÏÐÏÒÏÏÒÓÔÒÒÒÑÒÑÑÓÒÓÐÐÍÏÍÔOOURQNNNNMPNMNJGAEKHEE?BBFR\my‹“˜››¥¤©¨¨®­®ª¬­¬¯³°µ²²±°¬¦¢™ŽŒ„{lYWNKMSUS_\ZaegfolonstmNJfeKA_\URNOmZHPHW‰qmA4HjnS20KuxedXyyv¥›g=D‹ ^ ,##-217Xdv_11)+(# "(1YgT6,)3?>73-$-.,;Fi‰“›¢®®±¶¾À¹®)%&'$*/)+4.,./2(1,10`pneMNPcefluwquv}{x~€€ƒ‚ƒ……Šˆ†ŠŽ‘•‘•—•—•–—šœ˜š›š›Ÿž œž¢›—˜’•”{tx}ƒ€{}~}wongbmyxz~†›²½µ±«¦¢œ˜””ŒŽŽŽ…€‚„sb@0/1178,*-*B23<9FHU<9CLF.-.7A()"2e”¥«ž™ŒkH0")NWt†””“•–“““Ÿž›¡›™š™™˜–“—•—”˜””—•–•˜””’‘““•““’‘’“‘ŽŽ“Œ‹ŠŽ‘Š…†„„™£®·ÀÃÃÆÄÃÄÄÅÆÀÅÇÇÈÌÏÎÐÏÏÐÏÏÑÐÐÎÎÑÏÍÑÒÒÖÒÒÑÒÔÒÑÒÒÐÏÏÍÏÎÐNNWPOMNPMLNOKFFIJEDFHCDGDESao}ƒŒ•˜œ¡¦¨©­­¬®«ª±²±±µ±³µ°­¨£›’†yl]VKHFPTX[``gaeiknoxyyqSX`VC=cZROBPbP;=1,'D‚ŒvbUM;VacP=2>xobRpusœ¨xFOp¥k"!'*2)/m’š{C0,(%"$ "+W‰zU2)& 1B30N=;USL^dpp{‹”–”™Ÿ¨¤®ž_7'& #(.8;,1/+*+1&//*1cttiRJP^flhtoquw€~z{|€‚‚ˆˆŠ‰Š‹Ž•”’”“••“‘‡––“–š—–œ˜›žœ¡Ÿžš žšœ—”•–’‰‚~{z~|€‚„‡„††…„~wwvtpxv~ˆš­¿¼³®¨¤Ÿš›—˜’‘Œ‡‰†„„‡vmZ403-0841++0@75@9JMUB@ILC1+,78+%#-^¦¯Ÿ’z’[S+&.Od{Ž’‘•“ŽŽ“— Ÿžžœœšš™œ—“™™—™™”——˜••™–””š”’–—“–’“–“‘”‘‘‘‰‡†Ž‰€…‘Ÿ¨±¸ÀÁÅÄÆÃÅÄÃÅÂÈÉÈÊÍÐÑÏÎÍÑÑÐÐÏÏÐÎÐÎÐÓÑÒÔÒÑÒÑÑÒÑÑÑÏÏÏÎÎÐÐNNQOJHRPNHJCJHLGDBABCJCMLLVgp}„Ž‘˜š £¥¦¨«ª§«¬®­­®®°²¶¶´®ª¤ž–„zodWOIKPOT]V]bddhenz~rqeXcT:9>U]WV>NVE4+#*Jq‡ujND]HYWRJC5Ev„jKFe{q€ªYX{•2!',76'0o™§ŒLP7%&#$2p|mM,/#"JZDLei^em`o~mmz„„{‡›µ´·c1'##$&*;QJKFE,,--'3;4,Wryj]SQ\aikpwvtv~~}~z~ˆ…€††‡†Œ‡”’‘‘˜’””‘•”“˜œš’–˜œžŸ£˜›žžš˜”—™““†††‚}zz€‚†„††‚|twvz€‰‘©´¼¿»´¬¨¢ œœ””—ŒŠ‰†ƒ††xi?/,/4.94./3-=3;98N^NA9PQD2-,;1('$+Oަ¯ŸŽ}‘”eY;''Pp”••”‘”› žžžœ˜™šœ™—–“–•˜•”•””–”–•–™™–—“”‘Ž““”‘ŽŽŒ“ŒŠˆ†ƒ„Š“žª¶¼ÀÂÅÃÄÄÃÅÄÆÄÇÊÊÌÎÐÐÒÐÎÐÎÏÑÎÎÏÎÏÑÐÑÔÔÔÒÑÑÏÒÓÏÕÐÏÐÏÏÐÐÑOOTRTLRMPHKNONLGA@?FGKGJOSZfu’˜› £¤¥¨§ª«©¦««¬¯¯¯·µµµ¯«¦Ÿ–’…{m^TLGGRSV[\^c]cekvzyuwgZR.57>UaXVBLFB1%$6f‡ziH?>\UUDOGX=?k}`XTc„qc¡oc~…†N# *=6*/rµšwq0&Wy^F^O(#(Ja`ciuosv‡—”ajtklps…»ÏÏ-)%,#)*29BMZ451*'0#*+/5c~uddORYipqoqvxxz|z~€‚„‚†…‡‹Œ†Œ’‘ޑޔ““”“•š”–ž˜™— œŸžŸŸžŸœ˜˜’•”•‰Žˆ}€€€…‡ˆvyŽœ­¶¾ÀÀ¿¼¸²¨¦¡šš›–’’‘Šˆ„†€„xvU6,/-+20+2-/1;37<JE?6--<-+.",I‡£±¢Œ|Œbb7+(Sy‚“”‘Ž–œ Ÿžšžœ™™–š™—›—–š––”••˜•—”–•’‘””“—“”‘”‘ŒŒŽŒ‰……„„ˆ…™£­¶¿ÂÄÄÅÃÃÂÄÅÄÅÊËÍÌÍÏÑÐÐÎÎÎÐÐÐÎÎÏÎÏÐÐÓÑÓÔÒÑÒÑÑÏÐÏÎÐÏÌÐÑÓJJOORNSKJGRNTLJMEC@EHLLKTRYiz}‹Ž•𛥢£©ª§¨«©©ª«­±±°±´´°®ª¥—Ž„xp`ROLIMTUX]Zb``eiuwpsvlaD,+'2NaXZHM<<0%4Z~y_H20@Hc]CGQjCHnpaVVw„jT…§zb…ƒ‹b1#$,>.*0i’¥ª }79xa56UF03EZ]kmhw~‘­¬rdufcmrƒ¢Ó×ÀR%&*'$&)-18<@4010/1'%)&.gwy^dKU[gjstsuvt{y}~}€‚|ƒ†‰†‰‰‡ŠŽ“Ž‘‘’“”—˜“”›—˜ššžš–™žžŸ¢œ˜œš›˜œ•’”“Œ†ƒ‹‚…†–„†‘ «¼ÅÇÃÂĽ¹®ª¥£”—–“’‘ŽŒ…‡…~xl>4/1//41,/,24>7:?;SXMOKSBF:41B*1+"%?…ž«§x„Uc=/0W}‹“‘“’’‘™ žžšŸšš˜™”™™—˜››•”””—’“’–’”–›”””““•ޑޓ‘‘’Ž’ŽŠŒˆ‰…ˆ‚}Š˜£®º¿ÂÅÄÂÅÇÄÆÅÅÇÊÌÌÌÏÐÑÑÍÎÏÎÐÑÍÍÏÏÐÑÏÓÑÑÐÒÒÑÑØÏÐÐÐÎÐÐÐÑÒÓQQPQNHHNIFLQOMKJCGFICGHMTV^lx‹‘•›š ¥¢©©¤ª©©¨¨­­®±°°²µ²²­¤žš”Š„xl^VSKHRRWVZ[d^_ekotrr{tH*-%+PVQMEE=9-.Y„‚b@9/2>GTV>=\yP\~se]czzfJgœŠe†ˆw4($&28,0W€œ“ƒ9)" #)Xb6(;;JSSdcafnt›¦¶´Š`dj^eu~ŽÀÔËw0)*).+/*222-FA446132)*-1dwzWfNW[`nmsszyy{|{}z„~}ƒŠ‰…‰‹‘•‘’•””•–š™›œ™››ž Ÿ›¡œž›žžœ˜–“–‘І‡…ˆ‹Ž•˜¦¤–“”­ÁÆÇÄÃÂÁµ´¬©¢™——––ŽŽ‹‡†ƒvV1210.)0403+13<;>/5MJbkdpqkowŒ™°±ºž[cp_jw|ЬÏÔ¡:2.-48652:8:839451/118`w_hQT\emmruxyyy{||}{x}‚†‚‚†…†ŽŠŒŠ“ŽŽŽ‘‘•Ž”˜™›š™œšŸžž¢›žž››œ ––˜–––•ŠŠ‹‘œ¤²¶¥š–£°½ÈÅÂÂÂÀ¹´­¤žœ˜–•ŽŒŠ…„ƒ…€tI62503*//*226+8A?8B]bD>P\M;7/>L)/.&"6p‘¦¬–o‡T`J:Jdƒ““’’Ž’› ›š—š–™›šš—•›—™›˜””–—–••“““˜–’“‘’‘’‘”•‘”’‘‹†„‰‚„‡›ªµº¿ÃÃÄÅÂÃÂÄÄÆÉËÌÎÏÑÒÏÐÏÐÐÏÍÎÍÎÑÏÐÐÒÑÒÓÒÑÑÑÒÒÑÐÏÑÒÒÓÒÒÒÔRRSVPPPSSOQTPNMIGFHCAAFMT^ck{€‹—˜žž¡£¤«¨¥¨§ª­­¬±°°²³²³µ°¬¦ ›˜Šˆ€pbVNJOPRTWZZ\_bfigpx…•š‡P9,,0IWSG>OHJFx“pWC85,.>GMML?R[\jrchsl}nWBP–škp“„pF.)/@46Kj‡{dI-<&0DG4#!+6W|~vpy€†Ž Ÿ¯¾²eXojct‡‰¤ÂÒºK/.0004411-442@686622/,,04\su`iUTdaiqpvw{}y€|„‚„‹‹‰‹‰Œ‘‘Ž“”‘“˜™›–œ™œžž¡¤Ÿœž››™œ™™–“‘–‘‘””™¥³º¿±¡˜¤±¼ÈÉÁ¾ø±¬¢ž–““‘މ„„‚}x`;14451/1.*,.34BI;7?c^J;GYD9A4AB-6)("1h‘¡³˜oŒ†Q^T?\kƒ““‘‘”•ž œžž˜š™›œ”–˜›—™“‘•‘–”˜”•’™—‘–’”•“‘“”’Ž‹ŽŽŒ…„‡‚‡„˜£©±¼¿ÃÅÃÃÂÄÄÅÈÆÈÌÎÎÑÐÑÏÑÑÐÏÎÌÍÏÍÐÏÒÑÐÏÑÓÒÑÒÓÓÒÒÓÒÐÓÓÒÓÖÔÔRRNMKGFLPRRJNDHKEHB;>@FMWbcn{€Š•’œœ ¢£¦§©©ª©©¬®±°µ²µµ¶°®­§ š”Љ}lcUPJNLUU]a`baakhnv‡Ÿ“rV@26:Lc\MFSPan“~Z=;4255AJJVNEFM\gphZrn~viJJ k]‚•YK>@?MLGljopD6C>JN:+*(18S€Œ…~…”ž©¬²¶½Q`cYbv“ÃÌÃt85766A@>7738:4=?864,.2'-,2Zop]oVV`djptvx{x}y~‚€„ƒ„ˆ‹‰ŽŒŽŠŒ“‘‘“—–›—œ—˜˜™ššœ›Ÿœ œš–”œ˜™—–”–—’’‘‘”¡«ºÂÅ»¨¡§µ¼ËÇÁÂÀÁ¹²¨¢ž™’““‰†‡‰‚uB120453/6151364DH=5E_]IAFXD??8F?5<,(#/XŒ ´˜|ŠQY\Dgt‹’“’‘“’›Ÿœ›Ÿ››œ™–š•—•“˜——™•”“”••––“•—““–‘““’–•“‘‘ŒŠ‹†„„…‰š¡¬µ¾ÂÃÇÃÄÂÅÄÄÈÊÌËÏÒÐÑÒÏÏÏÍÐÍÎÍÎÏÎÎÐÐÏÏÑÑÑÑÑÓÒÒÑÒÔÑÑÔÕÓÑÒÔNNRMDGHOMIMPJJIIFDC9<2/>B+%$4J{¦«‰ŒŸ¤ ¬§¯¼°ˆS\jYZbeŠÃÇ‘7,1&,2(41.13.-061A=<;.2/,+(&JppoffTallqqpvzy‚z}~}~y~ƒ„…‚…Œ‰‰‘‹ŽŽ‹ˆŠŒŽŒ‘”–•—˜——›š–”˜™œ›œ›˜˜•“š˜•“’”””‘”—–œ¨µÆÍÊÁ·©©³ÁÉÇÅÃÆÆÀ®§£š—•‘—’‰ˆŠˆ}uB6.6213-,0+-0/28LD;8R[TH;?WipaVOKSJ+&&7G3(/@T•¶ª’‘¦¦­µ´À¼¬_XbZX_av Êª?)$$)41-,,154018;UOXH<@XG4FC;/3/"(#,;s’¨¤~ƒbJknuƒ“”‘”“˜Ÿž››š››š—š™–“˜”—–•’”“Ž“™—•–“’“Œ”Ž“’Œ’•’•‘ŽŽŒŒ‹‡‡ƒ…‚‘Ÿ§³½ÀÂÃÄÄÃÅÇÈÌÌÐÑÐÑÑÑÑÑÍÎÍÎÏÏÏÑÐÏÎÑÑÏÎÎÐÑÒÒÓÔÓÔÏÓÒÔÓÒÓÒÒÔÕHHLKFHJLIFCIJJD?=AD>B@ELU[es}…ŒŽ›š¢§¤¦¥««¨©©ª­±¯¯´µ³´¶¯¬¨¡œ”‹Šzoc\HCMKMRUQ[Z_i„——rkp|x`?6DB8RTgy†|M%++-Zd4;E808IF5F_A=BOTBMWM|p7@K_f†Œrx™w}I2JŠbB<,/2<.((6F[Zgy~Š—˜¦­°Á¾Ÿ[]m`Y]f|¬Ào0*%(+*+=@03011*.4>/ND><1-6/'&*Flfi\wX[ceopqrvy}{|€‚„‚‚€ˆ‡‹ˆ‰‰‰ŽŽŒˆŒ’ŒŽ““‘”“”™—•““™˜™˜™ š™““’–——”–™–𙥥°ÂÒÏÎÌÁµ¬°¿ÍÍÊÅ¿¹®©¡™’”މЅ}h7.../-.02/4--*16AHB9JPZQHB@PB8BD9511*,$/>p¢£}w‹fNo{w†’’”“—›Ÿš˜™—˜žš›—––™––’•–•–’“—”•––”—““‘‘—‹’–Ž‹ŒŒˆ‡ˆ}…„‘ž©¶¼ÁÄÃÆÄÄÄÈÊËÐÏÑÔÓÐÐÐÐÑÎÎÐÑÑÐÐÐÏÐÑÑÎÏÎÒÏÑÒÕÓÓÔÓÒÑÑÕÑÒÔÓÓÔIIFHHIGCFGB?CDECB=F=CBGNOYbq}ƒŒ‘–˜œŸ££¤¨ª©«ª®¬¯®¬¯±³³´²°ª¤¢š–Šƒyp_TOFHNMU^TVWfx’togw„ve=DCIB;7]SA>EMFHARyq;2LXt†€o|{‰e7j™S/8,(,?K26DWfmejt~—¢¦®¸Ã¿±e][VYZ_ršÂ•:+',-84/6D82.353,3=8JA=B-.40,)*Acdg`{YQ^ivnmwwx}~€€‡ƒ‚„ƒ‹‡†‰‹ŒŒ‹ˆ‰‹’ŠŠŽ‹ŽŽ•““••”••—’’—œœ“”˜—”‘–”•››™›š£ª¯ÄÔÓÎÍű§®·ÇÌÍÄÂÀ¼¯¥œ”’“Œ‹†Œ‡ƒ|N/.7-1.,8/-72/,35FC2A14','&Fn‰¡¬…qŒfOvƒˆŽŽ‘’‘“›œ˜—š›—œ›•›š˜––š””“•—˜–”——’–““‘’‘“‘•““––‘’ŠŽ‡Š„„‚„†–£«¶¾ÄÄÆÄÂÄÆÉÌÍÍÍÒÒÒÑÏÎÐÏÏÐÏÐÑÑÑÏÎÏÑÏÍÏÎÏÐÒÓÖÖÔÕÓÓÒÑÓÔÒÑÓÔÕJJEJGGHK?JCEGHIEIAB>>HMPLV`px|ˆŽ•››Ÿ£¥¦§¨©¬¬ª¬¯°¯®­³³±µ±­¨¤ž—”ƒzo`VJHDPPQQQ\]xŠ“qplk{†v_4?B@Womp|~h6)0:;5;TSHCD@PA51T`KJ9IOD?PguT4ICQ{€{yitmƒzU„§’:$1<(!+BY]ce`lagx‡•Ÿ›¨¶ÀŹvWec[ZYp“Áœ>)-*%*7604877+2;00.A4DLMB67/.0+'Bjbe`zdJ\bimuuu||~zƒ~€…††Š‹ˆ‰‹‹‘‘ˆŒŠ‹‡ˆ‹‹Œ‘“••’‘’’–˜™‘•””’’“——˜–š›› ¡¥©®ÃÏÑÍÌÀ­ª±ºÉÎËÍÅ»¾²¥œŒ‘ŽŠ‰Žƒ‚p=4-.+2.0210.+5854>A>GKPQQMDKJ8/D96?23*+()?pŠ­t’hIt„ƒ‰Ž’•“š›š˜šŸžšš˜•™˜œ˜–•••–œ””“”“””””’’‘”“’‘’”“‘’Ž‹ŽŠ†‡‚ƒ„†–¢®¶¾ÀÅÃÄÆÇÈÊÎÍÒÎÑÒÑÎÒÑÏÐÎÐÏÑÑÐÑÏÏÏÑÏÏÐÎÑÒÒÔÕÔÒÓÓÓÑÒÔÔÕÔÓÓÔMMJDFJ@HEDGEDCCEA<>>BCNNRT^pw}„•˜Ÿ£¥¦©ª©§ª­«®®³°°±¶²³´¬¦ ”“„{sbTHEENSUUY]nˆ›fgfn†€wX6=ETeh_mzxX:,7;42?XN>ELGFF=khf\qeS\bgjsov‚}z‚{ƒ}‚~ƒ…ˆ‰‰ˆˆ‰“‹ŽŒŒˆ‰Š‹ŽŠŠŒŠ’Ž’’’’“‘•““’•™”“–›—››žž«°ÄÏËÑϾ±±¸ÉÓÓÐÌËÀÀ´¤™Ž‰‘‹Œ‹‹€M4)-./.,:8321--2C8?<;CHPLNFBID88C9;:,0*'''7n€®q“uEq„Œ‹Ž“’œ›› šœ™ –›Ÿ–•™š˜”••”––’•“”••”—“‘‘”‘‘“”‘‘‘Ž’‘ŒŽ‰‹†‚ƒ„ƒ…™£®¹¾ÃÅÄÅÄÇÊÌÍÎÒÑÑÑÑÐÏÐÐÐÏÏÑÑÓÑÏÑÎÎÎÐÏÏÑÕÒÓÔÖÔÓÕÔÔÒÒÓÓÓÔÔÔÔCCFADB?KBGFDCHEA=?A=GHDJN\^j|€ˆ’”œž¢¥¨¨ª««¬°­®°°°±¹¶³³±¯¨¦›•ƒ~mbSEEGMJRQWj–ˆlkhfo|uV5LV[JFOxhbE-76<08EF?;EPFRu“”vq|“;')1Ii~…‹~qmitƒ‹§¦¨¸ÄÀ¨VZrVXcfv§¾r*&)$&(7MQA410/(+807148GHHGA0.2&,(5eh`]o_LQ[agxptyyy|„{‚‚‚‰ƒ‡…ŒŠ‘ŒŒ‰‹Š‰ˆˆˆ‹ŒŽ‹Ž‘‘‘‘”’Œ’•’’Ž‘‘“•›™›ž¤¤²¶¾ÊÍÕÑËÄÂÅÍÔÊÁÁÀ¿Àµ¨•Ž‰ŠŠ‹‰Š‹ˆy<*)/+.4-9D424,.085=@FLJQLVCALL?BA=G900,&'&/k{œ­u}Kt‚ˆˆŽŽ“—’”œ›œœ—š˜œšœ—˜˜—š””’‘“‘““”’”–—““””š“”’’’‘”‘’ŒŽ‰ˆŒ‰„„}‹—£¯·¾ÃÄÅÆÈËËÏÎÑÑÑÑÐÒÒÏÐÏÐÏÒÑÒÑÏÒÏÎÏÍÏÎÐÒÔÔÔÔÔÔÔÕÔÕÓÔÕÓÕÖÔÓÔJJA<;??F?C@GEIJBCC@?EFCGOU]i{ƒ‡•œœž¤§§¨¦¬©®®­­²±²³·¶´³®®ª¤™•…}shQJGHJORT_~’‹madgep„wkT;JXD49T|hVD*7<54IQA9?>SE<@QaNJ\KNBEEHKSOREAJLCKE;G>0-$$'&*^{š®›x†~N}„†’“Œ—œœ›œ˜›š›˜—™”™”——™”‘”•‘•–š“–”’”’’”“Œ“”•‘‘‹ŠŠˆ‡‚…}€‡–§±¹½ÂÆÄÈÊËÌÌÐÐÑ×ÔÏÑÑÑÎÎÐÐÑÑÓÑÒÐÑÐÑÏÐÐÒÒÓÓÓ×ÓÕÕÔÔÓÓÕÑÓÖÓÔÓÕGGBB>@?DIFSV^ivˆ“™›œ  ¨¨«­ª­®ª¯±°­¶µ²µµ²°®ª¡™–‹ƒ|neSEGJFLMWt˜o^`ddip{wkJ;HD6'4kxhX:27C2?MFA<9<\J?TQkgJ^]TSHKKRNHL\qT883K_wxrw‡”Œk3*+*(Cbxˆ†›¢·½´®—«¿È¿ÂĶwO^qW\ltж´E-0.(&**B_kL2030'*:5765@DDVOM/*,.**;[kWWfgTN\^aorsyv||}ƒ‚€‚‚‚ƒˆ‘‡†‹Š‹Ž‹ˆ†Š‡Š‰‹Šˆ„ˆŒŠ’ŒŒŠŒ‘”‘Ž‘‘™šŸ™œ™–•”  ž£­¹À´©¥ œšŸ¤«¬‹}~€…†‹‰„y=0.+0/+2569//01/846EF>HIUPZHBGXPCA;JE//$(&%)T›¨¢u~„]}ˆ‹’”˜˜š œœš›™š››•–›˜—›•“”‘”••™˜™””””—•”’Œ‘’“’“‘Ž’ŒŒ‹ŠŽ‰‹€‚ƒƒ‹™¦°·»ÂÄÆÈÊÊÍÍÑÒÓÓÓÐÒÑÑÐÐÐÓÑÑÐÐÓÑÒÐÏÐÒÒÑÑÓÑÕÙÖÕÕ×ÔÓÔÒÕÔÕÔÒÑÔFF=;;?>:)(FA;87ME@?AbvVS``VXMPPPF@HmtSA;:BUXWm„‹ƒ^+'Sscem„¤°ÆÊŽ¿ÏʿƳW_m[R]r€ªÅu*((-%'**@_qR5+-3**;5486=>GHGD4.5,'+9UgPYib[HV_dlqsz}|{„ƒ„~†„ƒ…ˆ„‰‡ŠŒ‹Ž†Š‰ˆŒŽ‹Š‡†‡‡‚‚‚…‡‰‰Ž’’’“•’““‘މˆŒ…‰ˆ”‘’˜Ÿ¢œ™™“‘–šš‘~u{~‡ŽŒ‹ƒX0/2+3105:3612436:4?@JE;?TLZHDFQPBA;FH7-&&%%.B~œ¥ tw†c~‰–‘‘“‘–žœš™™˜˜˜›––š“—•–”•‘‘“”—™““’•˜••‘’“•’‘ŒŒŠ‹‡‡ˆ„}Œœ¤®½¿ÁÅÆÊÌÌÍÐÑÓÓÒÑÏÒÐÐÎÏÑÑÒÒÒÒÒÑÒÏÐÑÒÑÒÔÒÓÔÕÓÔÖÖÓÕÕÒÔÕÔÕÔÓÕFF=><;9CA=EA@FCDFC:9?AMNJSYn}€Š’—œž¥¤¥¨©¬¬­¬­°¬°­±´·¶¶²±¬ª¡–~yn`OPEEIPYu“‡l_[\acitއmE6/*(0Wl^j<01B8:?CC7937@FC@_`Saig_WUXJPDB[wmK429J_Zfƒ‚}ˆ~U4*:p†i^o…£±ÅÊÏÎÌÒÈÄ»ŒT\ifZ[d|™Ç‘/%&((*-(7KbI,+,5,'48565CJQFHL7//-*,=`pOXd]THTX^jqps{{}ƒ‚„ƒ€€„ƒ…ˆ…‡‡‰‹ˆŽ‹ŠŒ‹‰‹Ž‹ƒ…~~‚ƒ„…ƒ‰ŽˆˆŒŽŠˆ‹Š‡‚†‡‡€†z‹Š‰–›’Ž‹‡ˆˆ‰Œ‚qfhvƒ‰Ž‹ˆx:/-3*.2+131/804619/9GKD>FUGXLIPZAA>7;>=DHBH@@:<;BHKOU\iz„‘𛢧¤§«©«®®©°±¯°²´µ·¶°³²ªª¡›–…zn^TIGBPSlŽŒm[ZY^gjr‚‘ˆeC45/*2^S2jkM45H>A@BB2;;4999;APOC96217?Ii„}eaejdjcPPPI@>H]{oWGaoWQ\wyaUXt‰”¯«vv‹§¾½ÅÖÍÏÊÁ©eVitfYak„°Æ|-)/0+-40-'%=QN0&*12/.12<=D4GBJSF,+,*/2VzFRWkNMJYaclowuwy…‚‰…{}ƒ|„‚ƒ………ˆ‹ŠŒ“‘Œ’”†s]FGOYemorzy|}xywu{upsnqrokkjponot~|}Šƒ‰rXMCW_bjnidls€‰’”ŽŠ|D-.0-+/.576-1/2-3;29?II3=GU=O?ISR@G339EC3.!'()7f“  l•|ƒŽ•Ž‘‘˜•‘™ššœ›œšœ”–œ˜˜™•›˜‘–˜“˜”“›’”–•’‘–“”–“”ŽŽ’’‘Ž’’Œ’‘Œˆˆ…€„‘ªµ½¿ÆÉÌÎÎÑÑÔÔÓÓÒÔÑÒÒÑÓÒÒÑÏÎÏÐÔÏÒÑÑÓÓÓÓÓÔÔÔÕÔÖÕÖÕÕÓÖÕÔÕÕÕÙÕ9997:@:@@JAA@<<;;EGMKSYam~ƒŽ•™™¡¢©­°¯¯ª±¯¯²³²±³´±²´®®©¢Ÿ•‹zn`RHFHl˜€i[VVX_qŠ”¡|_E>/9>12=6Mn{gnl]gif[LMN?>MXzxayyN:Gas`J:_‰˜š}sŠ«·ÁºÅÐÑÑÀ´zM`okb\d¥Ê®<$'0122:3&$,6JN=0),5,*2.98A1DBLOK,*''-6Up?KXlWMESYfkpvvxw€‚…†ƒ~~~ƒ‚‚€ˆŠ‰‹‹‹‹ŒŽ’Ž‘‘q[UTVMIHMU\ahhkkkjqosga_]_Z[TQXZ[cgpvvx}ztc]ZT[ceotpps}Œ‹‘“‹‰a5(5/-%1.5=3.531.6<6;FKN7HOS>LEQTRF@,.?UG<1$),+6b‰œœ‚w’…‚’’‘“•••“œœ™˜¤¡š—˜—–™šš———“——“’–’‘““—•‘‘’‘“–•“’“Ž“’“’Œ‡ˆ„‚€|…’¤­·¼ÂÆÈÍÌÐÒÒÓÓÔÓÔÓÑÒÒÑÑÐÐÏÏÏÏÒÑÑÐÒÓÓÓÒÒÔÓÒÓÒÕØ×ÕÕÔÔÕÕÖ×Õ×ÔÕ;;6=675A?B@DBD?>@@A@>KOOQbdr|ƒ•™›£¤¨«­¯¯²²´±²³¶±´³µ´±­¬§¡›“ˆtn]VLCY‚ž‰k\PVT]g}—“ž‹wZ<64AYVD.$)1/6@NM4BKU16H8;95Hu‘‡lkfYgosfW^UC:@M]{ƒ…}HF>]LAA@So{zxw{‘®ÀÈÇÐÕÕÎÀ¤WWhqXadv”Å»`&&(0,022/-*.3>IG0'*+1,5589?7B?LRN/%&*(2Rm?RRcXKIQ^fkiwxwy~‚‚‚{„{}ƒ‚„ƒˆˆ‰ŠŠ‘Œ””“skdndbYZSRWTRV]Z^fblpjhgf`c`WYYa^fopswuu{tlonhdltƒ‚~x|ŠŠ“‘‹w@-*-0,1.2472,**/4;32ALB=1;SW@PHOQO=:++:XN@1'.1,5Y„ £‰~‘‘„‘˜•“•””•‘ ž››¤œ›˜œ•š˜™—™˜™˜—•–“””˜–’•‘‘—•‘’ŽŽ‘Œ“’Œ‹†„ƒ€zˆ’±·ºÃÆÉÍÐÒÓ×ÓÓÒÓÒÒÒÓÒÑÑÐÑÒÎÏÑÑÒÓÒÑÐÒÓÔÓÔÔÔÕÔÖÖ××ÚÖÕÕÖØÔÔÕÔÔ::76595<;8??E>@@@GQPU\eq{‚Ž’—œŸ£¤ª­±®¬±°¶±²±²°³¶µ´´°­¥Ÿš“‰ƒwp_QLU|žŒt]WPTZZtŒ–žŠ…tX@/9QO<@/0(+9>LNY6CNH64X?<87CuŽrŠoad[^hdfpjbXMQMXhŽ˜y?76NF.9D[u„‚„†‹¿ÔÙÙÔÓȹ„M]iVV_v”¶Å„2-1,,73.2,**+09ID4-68-05479@55>JPS9*+/(4UgCRSbYSMS]Yeiszy}}{ƒ€}|~}ƒ€…„‰‡Š‡‰‰Ž‹—“‘“‘„xzx}|tnkb^_^a`jliqsssnpqnlpjlklomkosz~ƒ€‚ƒ‚}xyƒ…ƒ€ƒ’‘‘‹‡T4*-2++-123;.+-+,/825GJA96=WMHP?JSRD:+17OP>2$&1*2Q|š¡}Œ™Š‘™”‘”—•“’žœœšš˜˜™˜š•—™——“—˜••‘—•–•‘””–•“•““‘”’‘‘‘‘‹‹‰‰…ƒ†•¤­¸ÀÆÉÌÐÐÓÓÕÔÕÓÓÓÒÒÓÒÑÐÑÐÑÎÒÑÓÕÕÒÒÑÔÓÑÓÓÕÕÕÕÕÖÔרÕÕÕÕÕÖÕÕÔÔ33:96>:;?=<@EGB?A>@CAJYYWZgqz†Ž–•šž¥¥©«®¯¯¬²¯¯±¯³µ¶´·¶´ª©¢¡›…xqYUZu ˜qaUNSR\e„Œ“‰{oT=0>U:8?82&+C<<9:;EVG37YU?=<=|˜€ncddcc^Yhvmjc\U^m|‡vVJFBR8ISo‚Ž‘ˆ†…|°ÚáàÕμšl]j_Tcu•ºÇŸ<,)0+20.41-3()15C><-+0*4=6:7++3/2Sp9SWVVIMN[`efpqvww|y„zyz~€~€ƒƒ‚ƒˆ‡†‡…‹“–‘””’‰}~|stolnnrutw{~||~€‚~}{}‚‡‹”“•›ž›ˆ†‚‚‰‹‹Š‹‘’‰u80-/.4.207;4/./123<;9GH878D[HLILKSUH917>]a?/*,/+4Qy—£yŒ¢ˆ˜•Ž–’–—“œœššœ˜ž—•˜™˜˜•š–˜——šœ•“”‘“”’’”–”•–‘‘’”’’‘’“Œ’ˆ†‹…‚ƒ{„‰˜¦²¾¿ÆÌÎÐÒÓÓÓÒÓÓÑÑÑÔÓÒÐÒÔÓÑÐÒÒÓÒÔÒÒÒÓÓÒÔÕÔÕÕÕÕÖÖÖÖÕÕÕÔÕÕÓÖÔÔ666:78;;:@:;B>B><=9BDJ\[_Vkv|†Ž“™š¡¡§©«¯±®¯²®°²³°µ¶¶±µ²®©£žœ”Šti\d}››{\PLNV[_r‡”zz{g]E7IH.)013=8@8@@2;EJPB,,(,4NhCJQYVLOPVbbeqsuxx~{zz{|~~€~†ˆ…‡‹‰‰Š’‘•““““ŒŒ‰†€€€}|€{xx{y{€‡ƒ‰…‚…Œ”‘‘—™™™Ÿ©¨´°³¶­£’ІƒˆŽ“’”ŽŒ}L2,*.1-/1-8<-.1.2/3<=>P@3:9F`JEKRISUNE/2;Ma=212.)0Mk•£š|‡£›—””——•ššœ™™—”™–›™—’™˜–˜™™•‘”’”’‘Ž•“‘””“’Ž““‘Ž–Œ‹Žˆ…ƒ{|Œ™¥±»ÃÇËÍÑÔÓÖÔÔÕÓÔÒÐÓÑÓÒÔÑÒÐÐÑÖÓÎÔÓÓÐÓÕÕÔÔÕÕÖÕÕÖÔÔÕÔÔÔÓÓÕÓÔÓÓ885?;=<;IIOWS``gs‡Œ’—𧥦«±¯°®°´±°³·¹µ¶¸µ±¬ª¦•Ž€vok‚¥œ€ePKNPTWmŒ˜{vvud]K?EC7KQ<.89-#4/.1@aL64FbB81?‰°•‡oO:VVafifmbb_VXaV>:@hyT:d]zz„›œ•‰†ˆu|²ÝÚ̬‘_]nfeo®´½}91-30796555-1''4B8<82+-03<@<@9B6:=1*-2:N]J?VTNSLHMX`aksqvuqvvvyw‚z€{|„ƒ‚‡‡‹‹‘‘–“‘‘‘—”Š„„…€}~~‚~|yxz€~€‰ˆˆ–™ž«¶¹¼¯¾Á´¹ÊÆÅËÊIJ£Ž‡‡ˆ‡Ž”˜ŽŒˆƒxC.++)+.042356,)**(739256.38@KfKCJNLSK86/7EGZL4&)2+2Odœ¦ „ŒŸ’Ž˜˜•––•˜šœœ›Ÿœœ›•–›™›™•š—›™›——–”••›“••“”™•“Ž“’““–”“‘’“ŽŽˆ„„†…‚{Œ˜¥¶¾ÇËÏÔÔÓÕÖÒÔÓÒÒÓÒÒÒÒÒÑÓÓÒÐÒÓÔÔÓÓÓÕÓÖÔÔÕ×Õ×ÕÔÔÕÔÔÓÕÓÓÓÒÒÔÐÐ5587;78A9=@CD:A?A4;?IMST]\bqz„Š“–›¡ ¤®¯¯¯±³³¯°·±µµ¶´³³®¦¨¡›˜‰}{‰“–…dJIHLRRUcŽ›ufgtkfd?@A2@p~A><.&&2)'3PgQ=8:^h4-F¶”…mG=4IQ\llwul[U\^K;>7M_pcmƒ|„›”Žˆ‚tx‚¬ÑÊ™m[jrj•®½¼©V..796:753626(,1-/-6>0<6+,.26:5=B6?CDNH:..18HWE8VPSPCBHSW`gnpptot|vx|z|y~{~€„†‹ŠŠ”‘’’””‰‰…ƒ‚|~€~w‚ƒ~vyz~}…†“œ Ÿ¦³­´²£¦¯¹±º»µ¯•‰ƒŒ—𖋉†[5,0/.)../6257/**),85<9>0536ENlM=NNORJ>73BL@@75=ETW[\bdnx~‹’œšŸ¢¨¨©­®°¯³²«±µ¶²·´±®®ª¨£¥œ‚‚‡‘|\KGGILMT[l‚—khhlbhUAJK8OŒƒFC-''.3%*BdcEF;9Ql?.B—±›nO1-BQWYZlyyk]\bH:R:CZ``iŒ€…‚’›•ˆ†xtŽœÁѹiUlsr‚¢¦´¬¢o2/33+155135520-0(/+3<6591,,/6<89B???FLC?-)*8BQH5[QWR;ENWZeijoquqwxvwwvy~v{y€|‡€ƒˆ…ˆˆ‘’—•‘“ЋІ~|{zw|€ƒ‚†~~€€~ƒ”˜–™Ÿ¡¨¡™œ˜ ž§¤£ ’„Š‘Ž˜–—’ŠŠu<**0'/+.,/C2/4,,+*(1.;I<-,*69QpLBUQXUIH>8CETTX[]`m|‚Š’•™ž¤Ÿ§¨®±­±´´±±¶²³´µ¶²°¯©§«±¦Ÿ”ˆ‘}cIEACGLPSj‰žlhnjaXDALW?f˜wP8')%-,(2Rfc8OF5AnPQL]_F@;6?:766=FPQY_^`kx‹’—šœ Ÿ©©­«¯°´±²³·´¶´¶·¸²°®§¨¬µ´š‹yiRA>@EEPOZtŽ¢ˆoifjmmO8=@TF|•bG=344/0)-Sj_:EcJ3i`?b¨ªˆp{…Y58KUZW@@W}ƒrYjsS_Lgdck‹š—“„™‰xo|°ÖÔ¾\_u~Š­”Ê»—y43-51/.-15587<92/-,23595=674012:ID;9;>D@I;10.7JTX;NUTI9CHN[^chmkoovqsrvqyxx{~€}ƒ„‚~„‡ƒŠŒŽŽ‹‘’”’ŒŒ‰………†~~z{zz„‚„}~Œ‹‘Ž•—–““‘Ž”‰‡ˆŽŽ•”˜™Š‰ƒi=/)02/0---2=0/413//56+8E4//.9CQmF=HGaUGB?DH=8T^:-1-63K`“¥ž›—™Ÿ›’–—”–›™ž›šœ›š›—š—œ˜š›—›˜˜š˜•–™–”•—“˜•–“”’’‘•’‘’’’“ŒŠˆ‰ƒ~z~‹ª¼ÈÎÓÕÕ××××ÕÕÖÔÕÓÓÓÑÔÓÖÖÔÕÖÖÕÕÕÖÖר×××ÖÔÓÒÑÒÑÐÑÏÎÍÏÐÍÎÎÍÍÍÎ<<72968;:93:<;78:67@CNUYX[[hr‰—𢢥§©«®®²³±³²²µ´¶³µ¶³­­¦¥¡£‹€p_PBADGLOXn‰ •lfcfptcF3797O‘Y6,+07+0+8XnQ1BjX9a]C}´toi‹r@;OQ]eA3J`jx“Nsj}tn`¤ Ÿ‰sm…²ÍÌŸ]n~…­©°Ö±”Q396::8:5;97@996902-287532-774441:C787BHGM;2.4:JRWAUWOC;DFHNZ`ininpnprrsswuwx€{z~‚‚„‹ˆŒŠ‹’‘“Ž‹‹„‡…†€€}z~z|‚~ƒ~†}„‰Š‹Ž‰‘’“ŒŽ‰Œˆ†‚‰•–““—•‹††{b=+2/000+--9?.1.33-.>2.:E727-9EUfB>JEaSJOEE@;8_^2-9../Fk’¦Ÿ“š¡ŽŸœ“•š•šššžšœš›˜”™›–œ›–™›–›–œ˜š””š–’–”—™””’’’“•‘’’“‘Œ‘Œ‡ƒƒ…{y{Ž «¿ÉÏÔÖר××ÖÕÔÕÓÔÔÑÓÕÔÖÖרÖÖÖÖÖÖ×Ö֨רÖÕÔÒÑÒÒÐÏÏÍÍÍÍÍËËÌÍËÎÍ44.13:8767;9<896798AJSQX_k\esˆŽ”›œŸ¡¥¨¨°±²²³±³³¶µ³·¹µµ®©ª›šŒƒzh]JFJELMPb…˜ŸhadlttgJ7A42d–Œ]2'$.52+:DgnI/8Y[KW\HŒ²mlV‡YJN_msN=:ST}‚vwT„„ˆ}{Qnšž¬¤ˆ—†qm°¾ÅŽf€ˆ¡¬¬Ì»‘f4/0:7-55414=557@5:/45:534014745764@D>9GF@F<55.5AQV@DVDL=?AMOQ\efihnllnssvt{xy€|||„ƒ‡†…‰†Šˆ‰…‹‹‹Ž‘Œˆ…‹„……ƒƒ€{}~}zy~~~}ˆ‡„‡‰ŠŒˆ„ƒ€‡‰“–•’“’”Ї~cN2'+/1-5203=:356?5/5;409B970/:FVeD;MHbXENCD=:<[b9+7852Bq§Ÿ£“– Ž›•–š”Ÿ˜™šŸš˜››Ÿ—˜™šœ™™›››š•›–š“˜“•–—•’––’““‘‘Ž‘’ŒŒ‰‘ŒŒ‰„†„~z}}Œ¯ÀÈÍÒÖØ×רÕÕÖÕÔ×ÔÔÓÕÕÕÔ×××××ÖÖרÕÖ×ÖÕÕÓÑÏÏÏÎÎÌÌÎÌËÌÍÍÌÎËÍÌÍ004/33726575:696756:ERX[\[Ygqˆ“–™Ÿ ¢¨­¯³°´³²µ´µ²²³·µ²®¬¦£˜‹~skXLKBDIKOoŸƒobgeq}pXH051?pŸ‹Q-+446.,0RniF-7;ZPXWRœ³rhCq”y\Zfy{]E?>:X…ˆmaŠƒ}‚fa{˜²¬}Žur©Äȇ•¨±›ÑÇY=7578966627<>>77L:644695:930<=?<<=5@BA6?EKKJ?=41@EXB@YOPF=FQNNXacgeghinsoxp{zu|y~}„…‚ƒ„…ˆ‡ŠŒŠ‹Œ‰ŽŽŽŠ‡…†ƒ„†‚‚ƒ€|~{zwvx{|€„‡Š‹‚}}{‡•™––“Ž–‘Œˆƒ]KN4&'2.,*//+92'-8@0.;726?;<31,Vi@/4,3+:e‰¦£ –£ š•—›•›—žœššššš›š—˜šŸœ˜œ™—–—–˜•˜˜˜•““•”–”’”Ž‰ŠŠŠˆ‚ƒƒ†{zs|Œ¡±ÂÇÏÕØ×ÚÚØ×ÖÖÓÓÕÔÕÕÔÖÔÕÖØ×ÚØÙØÖ×ÖÔÕÓÔÐÓÏÏÍÎÌÎÍÍËÎÊÌÎÎÐÌÍÍÎÎAA33-03167??8:<4584@HR__XTUhq}ƒ’–›ž¡£¨«±±³µ³±±´³²¶³·´³±¬ª —”Žxk[RFDCAGZ›—necdfsyfJ<5;7Kx¡ŒK&%13.#"8_o]C640TSXTU©¶…b9Y€Œnlq{wdEKAB7b‚…„‹vkp…fK† ’n•vwŒ®ÌÊ–ž Â‘È×°e>964686:358;7<=>D76>GD;7=FCC::25=DOG:YURC7GIJHTWaelkijhlpspwuuuwy{~ƒƒ€‚ˆ‰„‡†‹†ŽŒ‹ŒŠ…‡……‡ƒ‡…€ƒztuwsuwyvw}{€xvu{rz€‡—œ–›”‘Œo:UB6-),.+''*04.*1997;:2257;:2/0?Q`YFIU@\T?QE?68EP_>00//5=i‰Ÿ§œ‹šž‘œ›”–˜—ž—›œ˜—›˜˜˜˜™š™›—œ››š˜–™››˜™—•–”˜•”•”—‘‘’”ŽŽ‹‰‹‡‰ƒ‚yvu}Žœ²ÄËÐ×ØÙØÛØÖÖÖÕÔÕÔÕÕÕÖÖØ×ÚÙÚØ×Ù××ÓÓÓÓÑÐÏÎÏÍÎÍÍÍÍÎÍÌÍÏÎÐÏÐÐÏÐ9933456202;485:2<77?OX]]XWVbq|„Š’’˜Ÿ ª­®°´´²´´²²°µµµ³´®©¦žš•ˆ‚wlWSCAAFK`‡¦ƒi^`jhonX?12=9Krž†G+&233*%=g[C81,)6O_Xt·¸‹ŠW8Kl‘~bo{†fC>DK2D`Ž™ˆ|_Xys5x‘„iŽ“y{’·ÒÁ§³ž©ÜÂv:4146-/359;86427480-..(35;0040,8=<?D75059?RO9LRJ=:@AGGTSXcellhffnmnpxuvzv{{€‚‡…ƒ‡Œ‰‡‹‹Œ‹‹Œ‹ˆ†‡„…‚‚‚ƒ~}}uutuswwotsrww|…Š” —–•‘”‰|E6Y=),1-(&+.//0,07<6:E8.58136:/,=Xm[DFSEc^GOD?@C=UX?06454;fŸ«Ÿ‘–¢•—””’œ›˜œ›šš™™œœ˜š™–—˜™œ–œ™š››š˜•••••“–™•‘’Œ“Ž‘ŽŒ‹‹ˆ…„}zztx…œ°ÃÈÑ×ÚÚÚÙØ××ÕÕÕÔÖÕÓÕ×רØÙÙÚÙÙÙÕØÕÓÒÓÑÎÌÍÌÍÍÎÍÌÏÍÐÐÐÐÐÑÐÒÒÏÒ22012859067.33//358EQZ`]\WXao|‚Œ–•›¡©ª®²±³²°³±´··´·¹±¯¬¡¡œ”ŒƒvnZPC?>EKd‡£}e[dmnkfE2*5>=Yr–uA/./5/.'Hd^@;61)-?Pa†»®‰”gC=[ttw€oGLLT8Ly‹„Ž„zk`uf:nŠ{VzŽu{£ÃÏ®‡š³œËÈŒ=0/.001,0500536:<7A:9,*)2973673F5:7:DH62D<;>8A?83536?LJ=IYR@9;GKNPQU]biihiinjjjtvwvv|„‚ƒ„†„ŠŠŽŠ’Œˆ‰„‡‡ˆ…„…‚†……€~yxuvzu||~}ˆ•™œš›˜Œ‰Œ‰‰a19V;++--3(/,,-614843LC74?7396A).:Jt[>HMC^fKHE@=@?N[C.=H778538B[}‹[E.,0),&4OcY?B>33+0=d˜»–}œ{\;Wƒš}r€pUZX]Tk‚nv€ˆ€~ebXNm…|Rfˆmu¦ËÑœƒ¶›ÎÇŠC4,3518<;835/779886>91,3,687/)20602787;5496;=69@=6:/67FD7COUBA3HEOSNU[ZZ`ecfogliqrsuzy~~€€ƒ‡‚…‡‹‰‹ˆ‰Š†Œ‰‡‡Œ„ˆ†……Šƒ…†…ˆ„‰‡€†…ƒŠŒŒ”‘™”™•–𓇋Œ’ŽˆA.<<:>@B:>CEB;166CC6AOSG>7>GUVNX[]Xceiglnloqryw{|‚}€ƒ…‰ˆ†ˆˆŒŽ‡Œ‹†Œ‹‹‡†††Œˆˆ‹‰ƒ…‡ˆŽŒ‘‘’—Ž”‘”••šš™–ž•‹‹‘Œˆ^-/?P6-0-++,3/-.=81:45@E64511,1=)-6Iq[EMKF]`JAH==>9M_Q/2/521\ˆ©¨¥š’¥œ™”“’šš—˜œ™™šœ™—™˜˜œš™š˜˜œœœššš–˜˜•–—’•’•“‘ŒŒŽ‰‹„}|{ux†°ÂÎÓØÚÚÜÚÚÛ×ÖÖØ×ÕרÚ××ÖרÙ×Ö×ÕÕÓÓÐÐÒÏÐÒÐÑÒÐÑÏÑÒÑÓÒÔÓÔÔÔÓÓÓÔ>>3/6525141/5403.5@Tfbibj_gmv}ƒ‡Ž“—œ££«­±²¯´²²²±²³µ·µ²°¬§¥›”‚~oZVG=C@Jb†–yeWacWXUMI:;CEKrf6;252+#*7Q`S74AE6/1A~¢¹†‹w‘sMI‰œ‡…zkiav†€\PRQ€~uŠrG71**+'12-2:43=678=71488038,+:Os^GSPJ^YHHHBBBDNWO42396.Y†£ªŸœ’¢œ—“ ›˜˜œ›Ÿœ››š™—›˜š›šš˜œš•›•™—˜–—˜–‘•”–““•’”•‘‹‹‹‹‰……€~|xzsu‚µÆÍØÙÚÙÜÚÚÚÙÕÕÖ×ÕÙÙÙÙ×רØÙÕÒÔÔÒÑÐÒÒÐÐÑÒÓÓÔÓÒÐÓÔÑÒÒÓÒÓÔÓÔÑÓÑ5542/271/1200110+3GWdilhebhor†‡ˆ’—›¡£¬­­®±³®´®²±²¶±´´««¦¤¢—‡yn^OG@JDKZy‚m\daY`YJB;GPJMeW-77--.'*5VbJ38@G59CBi¼‡‰^v‹vSk¡š„gmf~‚lBGWV|ƒu“zA78^…}frŒ|ƒ­ÖÜͶÖÂ~P8,))--5950150,.0>95/;;32&23B3//327212058:<>9<7<;B;?8/,66??9CBK>:4;DJGFOTZ\Xajgglmqotsx~|€y‚„‰‡‡„‹Ž‡‡‡‡ŠŠ‹‰Š‹ŠŠˆŽ”™˜—˜š¡Ÿ¡¦¡¢ž›žžœ˜•“‘މZ2,3TD42"(++04.03561C979>@2257+5>+1;RtaELCB_XTLMBFFBPUM940;8(R‹¤¬§¢–¡¡—Ž˜œ™›ž››œ™š•—œš˜™šœ™•™•š—™š˜š““—––‘“–•’•‘‘ŒŽ‘‰Ž…„„|~|svvž¸ÄÎÓÙÚÛÚÛÚÙØØØ×ØÕ×Ù××ÖÖÕÖÚÔÔÔÓÔÑÒÑÑÑÓÓÕÔÓÔÔÔÓÒÓÓÒÒÒÒÓÒÒÓÑÑÑ4407371203//.)-/27NZfigigfhk|}ƒ‰Œ‘˜™¢¦¬¯­°±°±°²²²±³¸³²®«¥ ž•Œ…~q`UJ>BHFUp}riqbZ_VGADWZPEdD5?:..)))>ZZL-2=D=8,3N”³‚z]aw‡k[‘¢ˆ{motsqZHMb\\pv‘ƒVPHj„qIv‚vŒÃÚàÈËЛI862.1/4446-2002657>::@9:-'.0892/68;2-0633<>;;6;>:D?@:,.28>?7=:59GFD;LTZWY^dhhgjmnnpu{~€~||„‚„ˆ…ŠˆŠ‡ŒŠ‹‰ˆŠ‹Ž‘Ž‘•’”‘•—™žŸ¤¥¥©ª§¤ žŸ›Ÿ››—˜’““”‘‹;-+3ND/-.+1,.3024116L68:?A11?8+38*2;Xw`DLPHbQMJOGEKTSOFA01@<.Q‰¢­©£–¥¢–‰™šš™ œœžœ™›––žšš–™–˜—””˜™™“™™™”–—”“•š’‘“ŽŒ„ˆ‰ˆ€~|zztwt€¢¸ÈÐÕÙÛÛÛÛÞÚÙרÖÖ×ÙØÖÖÕÕÓÔÔÕÚÓÔÓÔÔÒÔÔÕÓÖÔÔÕÔÑÓÑÕÓÑÒÑÑÑÑÒÏÏÐÏ112.-4375020/0325@P_fffcgcily‚†Œ’Ž”›£§­­¬®°²±°®±³³±µ³¯¬¦§¢˜—ƒyndYNHFCMTevymioZMNFBJ^jXO^@564*)&)*@X\G*,1D>823GŠ¥lmuctfxy\uš’‚jpyif_NOXTVa„zƒ`cct†i3gƒbv¥ÏÚÏÑ¡R513.13555;<=260,549;;9=61.+03?82567=4030-+2;;864@9?H:64013<4--*(,-EcaF1(/<@>77AЧhdhVrxrrai…™„upylce[WOHLU‹~sp€…g/61698/,-/-07=99588DC;71107:BB;;AF623=INC<:AJQWYZ^bfkjjrrv|{~……‚„‡„‰ŠŒˆŠˆ‰ŒŠŽŽ’””˜œœœ––—˜ž©¤¥¦ª¬¬®«­¢ž¡žœ¢›š˜•“’—•‘„C&'-7I5.+1+/321/56::G=;@K4243?071761?asaIOPMiNUMGDEPLYM>K:;:7-IŒ©¯¤ —¥¢‘‰››——¡ž šœœ™šœž›™–œœš—–—™›•—–––™š˜™“‘”“–—•”‘•‡ŽŽ‹Œ‹Š‰‚~{yvpvˆ§¾ÇÒ×ÚÛÚ×ÙÚÛØØÖÕÔÕÕÕÕÕÕÒÔÕÔÕÖÔÔÔÔÓÖÔÔÕÔÔÖÓÒÓÑÎÏÏÐÐÎÏÐÏÐÏÏÌÎÍ--112.-5917/13306GMR]hnlecldHIOEX]_``V;5>4.)'(/Id[A+*.30,,111087987::E<73021:=?6A9E720:GOE916BFOYY\^begjlmtu|‚‡ƒ‚††‡Šˆ‹‰ŒŒ’ŒŒ‘•’“™›Ÿž›žœžŸ¥¨§ª©ª­§¬¨¥Ÿ¢¤¨£ŸŸ˜–—–™’r/'*)>M4-)0)-5,/1;6;@;<>=;10;AG>0-44/E\qeEPRNiTYL?EDLK[M3MB?7<.RЧ­¡£—¤£“™˜™¢ŸŸž–œœœ™šœ˜™™™˜›™™–˜›™•–—’•—˜’™•”’Ž‘‹‹Ž‹…ŠŠƒ~yzwqzŠªÀÎÓØÚÛÛÚÜÛÚÙÖ×ÖÙÓÔÔÕÕÔÓÓÕÕÕÖØÕÕÖÕÕÔÔÔÕÓÒÒÓÒÐÒÑÎÑÏÍÐÐÏÎÏÒÌËÌ--.2.1033/0001:46AP[gmkjj`gqx„ƒ…ŒŽ—™œ¡£©®°¯¯®®±°±±±¯´²³®¨¤£š•‹‚wjaULBCLNVYank[cl^JEGKMHL\`W@4A:12'#-Bd[E00447?GJOx¡[gnZL]ir|nxyŠw~‚vpk\LILv˜~aeˆ…”l.Dn`t…›»Ã[/.?C1.+,.1//4405.1/5HH@476.,-/69;4-150;22-22231:972946;@85826?=8<<<;75BGNK9238>FUKRUabb^hmnqvv{‚††‡ƒˆ……Šˆ‰Œ’‹“••“Ÿ¥£¢œœŸ ¡§ª®±ª¨¡¨©¯¤¦¥££¢••’š—’‹`41--LP11)0..14+3=47Jas^8.A755&&.@dT@,'/:7?LPOn›WmnG6CEF^{„{n…‡|xwujUFPu¢‰c`€¡m.;`[}|†Ä¾U26[A1,254745:1.88077`A>;;7/(&178=32/03?530.04302;5;?7<;A33=53C=:6CA@7.7FOL?4-47:DEMVZa^_dglpsu}~€|„€†ƒ†„‹ŒŠŽ‹’•‘“‘˜—–ž¡¤¨¦¨¤£¤¢¢¥¦©§£¦¡¢ª©¥§¦£¦ž–›–‘„B),,2RD-0/1:640-593=@4-05K7/:0077Mb}kONKJqJVO@PMNMSA3IY>17,>‰ª®§žšž¡‘•—˜£š¢›œ™š™›’—œ—š˜˜–“–””˜••”–—™™™”‘•’“‘”“‹Ž’ŠŽŒ‹‡ŠŒƒ…~€~{tsu€š¸ÊÐ×ÚÜÝÛÚÜÙØÕÒÕÓÔÔÓÔÔÕÖÕÖ×Õ×Ö×Ö×ÕÔÔÒÒÏÑÎÏÍÍÍÍÎÎÍÏÎÌÏÐÌÍÍÍÉÇÈ//.-1923--,-,35,6DUUW\`_``foz}„ˆ‰Œ•››£ª©®±±¯°°¯¬°°°³µ´±²«¨¤œ“Ž…p_TIGEHQTZac`eVKI<5-03@ZUD,26>7-;Q[™VbeF93975JUl…‹‰Œˆjp€‚‚xiddš”}w|˜ƒn^MHy]BVQ:@kF233853@:3201767/9A=<=:=,/-18>:0385.2>72.2.6245;:4C;77889:7;;;6:C@B6537?>I842*15?8+02576D[ziROSMsMMM@PXIRVF/JW<8@*A„¬±«¢›£¤Ž•’œž ›Ÿžžš›–™—”–˜—˜˜›•›–™–•–•—™”˜“•˜“”•’ŽŒŒŒ‹‰Œ‹‹„‰„}†{uvsœºÉÒÙÛÝÞÝÛÚרÔÔÔÒÒÒÓÓÔÕÕÕÕ×××ÖÖרÕÓÓÒÑÐÏÍËÎÑÏÍÍËÌËÎËÌËÊËÈÇþ00.256960)+.2917AAQQY]`_`cdoy‰‡ˆ‘“𛤍ª«±­®¯®®±µ°¯³´³³¬©££ –ƒxq\OEAAHNPYjctvX6876;981NƒtX2/;0>8110..)<0:C533;?;85@F<905JCHEB/11-+55=>EQSVX]lmqxzz{|~‚€†‡†‹ˆˆŠ•‘’—–˜–—›¨¥¤¡¥¨¦¨¬¬ªª¥¦¨ª¨ª©©¥œ‘’’—Š„pR,%+2E@15629;:654579:;BL84217817249.;?MhuhGJPMoDMFAPWOFMK7FXG4<,E‰­®£¢œŸ§‘Ž’——™š–›Ÿœ™œ˜—–“•–—š˜—˜˜—š”–““˜™““––—“•ŽŽŽŠŽŒˆˆˆˆŒˆˆ‡†…€||wtux‡¥ÀÍÓÙàÞÞßÚÙ×ÖÔÓÒÒÕÓÔÔÕ֨ררÙÖÕÕÔÕÑÐÏÏÎÌÎÍÏÏÊÌÊÉÇÇÅÅÃÁ½»µ³¨ŸŽ33-/6455201/11.,26CLUZa`eisw„†‡‰‘–ž£§ª­®¯¯®®°°±²²²³³­²­¨¡Ÿ–Œ…{nZOG<@BMNW]qxO;21483<-1Z}_fI039948@LDOK;30-*))07?@FRQ\cjouquvtv}~}…ƒƒ‚ŠŠŽ“’”“—“—”šœŸŸ¤¤©¦©¬¬±¨«¨¥¥¨¦¦¦¡¤›’‘“–Œ‚nR-(+/?9338<=7:7964:68=@RA1.884089151=HOhtePNQNjIPIILXNNXI3KZD46-B…«­ œ£¤”“–“’”•——™˜•˜˜™”–”—›“—™•š”˜——””•˜™˜—“’–“’ŽŒŠˆŒŠ‰‰…ƒ‚~}}vuu{®ÂÍÕØÜÞÝÝÚÙ×ÕÓÒÑÒÕÓÔÕÕÖ×ÖÖØØÖÔÕÔÕÑÐÑÐÍÎÎÎÎÐÊÌËÊÈÄ¿¾¸¶­¨ž‘qa4432/11.,,,+-67042=EI:1JHBC2-.84.)(Kˆ˜„x‹tM9.NSKQf\azYc|x{Š”—‡…‰or…ŽŒ™¥ƒQBHV]U]O?564<35068;6767123;8>DCFM@23-*-7E>//24:55393../7330.27<;68=>C:49<=<8454./3=7?OPWYbdfckpry|~|„ƒ€†‹Š‰‘“’“’˜–œ›¥§¦¨¤¨«­¥¡¤£¥¦¥žŸ™š’“‘Š}nX/,14B9549;<6:9<888:16FN<859A0/2::976EUdqgPXWLfISKNO[RUWN?O`I<8.F„°« ž™ œŽŽŒ‹Œ’’’“““•“—˜”—™“•——”—˜–““•‘’–—“•””“Ž‹‘‹‹‰ŒŒ‹‰‰‰ˆˆ„€‚}yxuv{’²ÆÐÔÚÝÜÞÜÚÙ×ÕÒÒÒÓÖÕÖÔÕÕÖÕÖ××ÖÔÖÔÓÒÑÑÎÌÏÎÎÍÍÌËÊÇÿ¶³¯¦™ŽƒtcR=;00.-,--./.,&,+05579CKY_`ghov‚Š”—™ž£¨§¯®¬¬ª°¯±¯³µ³³°²­­¥¦œ–‘ƒ{lYND?C@DKb{qSA898,-5CIHhSBXF509B:@C@GCFB3))852'-?‚¢†wŠ~[>9E]NJRW\xm]n\l…™•š™zyry‹–Ÿµ¢]FNlokyZ93307023261152863989=BCHQA88018:I?7/35>3:9<;2352787525:;;>=BA99589=:?9983@OIJL\V]X[`kpvptzur€€ƒ……‹‘‹‘””–˜—™œ¨¤¨¦§ª¨£¡ŸŸžž£™Ÿ¡˜“ŽŒŠ|jU1,+5@;=@:6<=59<58>;6@TI9:4;H37<35.3<>TbogJXURbNZLJOaQVTJ?S\G?;1P‰­«ž›™ž›’Їƒ‚‰ˆŒ‘Œ‘‘‘’”›“–’˜˜•––“˜“•”–”••˜‘•‘‘ŽŽŒŠˆ‰‰Œˆ„‹‡‡‡~}zwst€ž¸ÅÏ×ÚÜÞÛÚÙØØÕÔÔÒÕÕÕÕÓÔÕ×ÕÖÖÖÖÓÓÓÓÑÎÐÎÏÎÍÍËÊÈÆÄÀº´§¡”‚taNB9310--,6//12520,*+47119=LU_aeikqz‚€ŒŒ–šœŸ¥¦ª«®­­®®«°²¯°±°³°­­¨¦›’‚xiZQD67AIdvbWFXe2-++CP`lL>MN>29AF;>AU>9E2)+76;$!0y¤‰xŠƒWA8@QMGEAVkyhq[]e…˜…rp{zŒ—›£¥vszˆwroB220.026<434338116788EBNN8/11.3?K>482631:48840337/*/2,8368?;<866=?A:D?9499ARIGIF==5<-4+48:>;BFFGEDAFMTZ^^forwqy{yƒ…ƒ‡Œ“˜—™—‘œŸ¢¦¦¤¤§¢Ÿ—›™šššž–“Œ‰ˆ‡vaA+-./9926896373;877196K@4347:46/386@>@UarfOWULbM[HNU`R\ZI8M\H<<.W°­ššŸœ›†‡€€ƒ„‡‹ŠŒ’Œ‹Ž’”‘Ž”——”•—”œš‘———”•“’”””’’‘ŽŒŠŒ‹ˆŒ‡†‹‰„~€{uvuw†¥»ÉÑÖÙÝÝÝÙ×ÙÖÕÒÓÔÓÕÓÕÕÖÕÖÕÕÔÕÓÓÔÐÐÐÐÎÎÍÍÉÈÅÃÁ½¹³©£€kVB4320.++)),)04.1-.+*,(*3,3479KB<893(+5-8,.-~¥Žƒ’Œ`I@AZXMDAP\zsx}eU[x“vSn{‡†ŠŸ ’”……™Ž‹e?.)+)+,//142994335BDJFNQ3+(*)5?S;3..//16:<70...51++0466489?>;>9@HEABBA4<>>LHGNMG==:>D<9@@=8C::>=1Y±°–𥕒ˆ€}xwx}‚„…ƒˆ‡ŠŒ”’•’•—•“˜•—”š•”“”””’‘ŽŒ’‰Š‡ƒƒ‰‡‚|xwqu|¯ÀËÓ×ÜÜÜÛØØ×ÖÕÑÓÕÕÖÑÔÓÔÔÔÓÔÔÐÒÑÑÏÏÑÏÏÌÊÈÄÁ¾»µ±¦›ˆfJ:4-+++..++//0,--10+/*,/*5*)2?@BT^__^[dtzƒ‰‹”–ŸŸŸ¥¬¬ª®ª­¬¬¯­°°±¶´±®¨¢ ›’‹peYC9AJl‘®ÈŒ^es w0-*)1FQUH=;?A;BAEDHNR^\Z``bg]fmrxytw‚‚……†Š“”“––”œž¢¦¡¡¡—––‘˜˜‘’‹‡†‚‚Ši\@9348556;787592?758:68M>37:7.-44./9A?>S\qmQTWTbSXLVIYPX^GAEKRCM4U޳±”œ£”“‡|qpwxyz‚†}„ˆ„†Š‡‰ŽŠŒ“•“‘“’•’˜–”’“•’‘•‘‘‹ŠŽ‰†……„‰‚z{{wusvy•³ÃÍÒÖÜÜÛÙ×ÕÕÓÔÓÒÔÔÕÒÓÓÔÓÔÔÑÒÓÓÑÒÏÐÏËÉɯž¸±¨ ™‡s_K<4+)$)))***-00-,/>30/),,1()'*.1?FLXcaY]dl{€†‰’˜ž£¦¬¬¯¯®­«­¯®¯­²²µ²­«¡ š‡zraTE=Lvœ»Ó¡Zf[‰¦f2'+./?SNZA46=?>989>;;@A5.66FIW^`\afdbkmnrsyuxyuwwyyƒ†ƒz}ŠŒ‹Šš›™— žž¢Ÿ¥©¨¦¤¢££ §¢›–Œ…‰†‰‰~k`A210839;8B8?C3:7A;8-7;PJ9::;04>82.8D7AO^sgMW^Z`VUGSNQRYWK;LIS=G5Yµ±’ŸŸ“†{q~rnspqt{€ƒƒ…‹……‡‚‡ŠˆŽ“Œ“’’˜•—Œ‘Ž‘ŒŽŒ‹Šˆ†Š‡‡…ƒ€|vsut µÆÏÓÙÝÛÙÙ×ÔÔÓÒÒÓÔÑÔÒÒÔÒÒÑÒÐÒÐÒÐÑÍÎÌÈÄÂÁ¼¶­¡™‡u_I@7-.5/%(&/.1/3//./10,0(()(,**&'/3@IKW]]X^^hu~…ŽŽ—›¡¤¦¨­«¯±«©«±­®°³°­¬¯¨ž ›‡ym\TIVy¤ÊÕŸRLKGƒ™c5-(&':K_ZC216FD879@?8<<;965@FJQSQGDKJRV\nmmknmty}~ˆ†††’ˆ†ˆ…‰†‡Œ††’“•–¤¤›££Ÿ £ž£¦¨¨¨©©±«¯¬ªª¢ž£¡¤¥ •ƒpJA>;7577?528;6559857;=A675501985/8J::S\lmRSPWjVTLJLZV\ZH=FIFKF:d™µ©¡†€sf{vlhmqsqtwu|zu|x{~‚ƒ†ƒƒ„‰‡‡‡Š•’–’’•ŽŽŠŠ‰ˆ‡Š††€~€{zvruv®ÂËÔ××ÚÙØÔÓÑÑÒÐÓÐÐÐÔÑÒÐÒÐÑÑÏÍÏÎÐÌÊÇý¹²°£™‡p^H90,$))+**(121<;<>22;>7349/-)'+&(-,)4?DINVQLMWhy€†‹š™Ÿ¡¦­ª°¬®®­¬®««­®¯±±°§¤œ”ƒvndl‡­ÒÛœIYWB?]²‹K@7A2'0F][LFABBC:CO724:@E3*/6.43)9h‘zwyp˜z`KLYO=93>GUe[KRo‰¤š€aJU|˜¤‰vG@?6?t£´ ¥“rD+()+/+/-1327>KHOWF1-&(.3JP323-1.-/63858-11001-,2532499:<975@B894321114JEGRZWRXfkknoxvz~ƒ€ƒƒ‚…‡…‘Ž‹‹Š‡ŠŠˆŠŠ‹Œ——˜š›Ÿ™›–™––œœ¡ž¡¥ž¤©¬­·¶»½ÁÄÆÁ¶¯žrZD@7947727=74532<7=>245*'38304I>;NXqmRSPWcQYMQSUR^bMJJQHG=Dež²œ¤™seyvsiimpkmqqzysywyzqw{{z}}‚„‡„ŒŠ‰ŽŒ’Œ‹ŽŒŠˆˆŽ‡„~{{wxnr{“³ÁÌÔ×ÙÙÙ×ÑÑÐÑÑÔÑÐÏÑÖÑÑÏÏÐÏÏÏÏÎÌÍÊÇÿ»°¬¨›ŠubP:/-+1.,):%+,3>FEBE99467:;5.-0)++(*+)3=DFKROLJRbu‡Š”™¡§«ª¯­®­®®®®ª®«®°®¬©¤’Š…vtq‘±ÙÕ—MDvfI:?;JRG8254RN2/)110//Bp€wh~˜~hKOR<0/79EifViŒ‘žƒbVYMvƒ—©˜ŠvaH-/Tz®µ¼¯“m=+(+/0000725:JHLWG30(*&8ST5-02135,04;66/16417,22702;9;6:C;28H<67430004IIQMNPW]gkrqv€€ƒ„€‚ƒ…ˆ…‡†ˆ††ˆ„Œ‹ŒŠŽ›˜•—šœ™šš™—›™˜œ—›››™›¦§¨«°µ»½ÃÄÂÆÉÊü°–kM71<5447551220844/:1)45F9119G?KYkmTRT`aQ\NLRLL]_HERP?J=?k›±™–Ÿ–znjtv}qermnlqsrpruvvsqvuwtuw~{{{‡„‰‹‰‹ˆ‹ˆŒ‰‹Š‰ˆ‰Šˆ‡‚†}{zzyov}žµÅÑÕØØÚÖÕÐÏÎÑÐÑÒÐÒÑÖÑÑÒÐÒÎÎÐÎÍÎËÇÅÅ»¶®¥ž“oVB4-/'&(&)1136@HNPKI77677@E3<14+.&+&)(8;=BHLJNHQas€†ˆŽ•›œ¥¨©ª«¬¬®ª­®«¬²®²®¯©§¢š’Љ‚}š¼ÚЙN@TQFB8QuW2(*-#(0;caWJ>99?MS>79<3ZV:2.5/75/>l‚ˆy‚qb‰”Žƒ[HF?+),;Pw~en‘›˜qkUQ_q~˜˜xi„‚]J;AV©¬³¯ž—u?,0/1.4.74:DDOVR@76*3+;V_2.014527129:82..0.212603159:99=5239E;59-05--@FJRQU`fjprw}‚ƒ…{y€‚„…ƒ†‡†„ˆ„ˆˆˆŽŽŽ“••˜”–š›˜›–•™–“–“•”–˜˜Ÿ ¨ª®´³¹ÁÁÁÅÇÇÈÇÇÉÄ·¡ˆjK=:3:6:34-63974.3/(*.1,6/5S@OXirRLTUWNdGRMFOZ]IJMPAHH@o£²˜š ’~zsizn{yofpmqtomvrorqsssuxptvuttv{{{‚~~„ƒ…‡ˆ‡‰‡‰Š……Œ†„„~€}zvtpt~œµÇÏÓÕØÙÖÔÐÍÏÐÏÏÐÐÒÓÐÑÐÑÑÐÐÐËÌÌËÈÉÁÁ¹²¬¢”†q\E701'$&)).2:30.4-@GR843.25:,011577-/0431+/244757<>A@:;;@81846936@;57;576=:6/2-41=5<7>UPSZgrWHQQXUcMTRLQ_]JPRTHM>Eoª­’š™~zzutz‚yrqsqoowuqsoptporpmpqsttvvyy{€€~€‚}„ˆ‚‡„‚…†„„„„€||yxsvt£¼ÊÑÓÖÖÖÔÑÍÍÏÎÎÐÎÌÑÑÑÑÑÑÑÒÑÎÉÍÊÅÃÄ¿¾·±©Ÿx_J@1,.*+/-35=>EOURUQOI@@?FIBA:943422.&*'-7;@JJFBKMYjv|ƒ‹Ž•› £¨ªª¬®­ªª«ª®©¯®®©©¦¡Ÿ–‘’ »Í®kG6@PVJ20V›w5(%')%!'9T]YJEE8=?E>/88:Cv[><<,:51>AD987825>6025FZXZos_KTP[T^EQRLN`[LKNQOSBIt²ª”›–Šx~u†~}€{utywqsssslpppornojolmiolnuorw{xxyw|zyy}‚ƒƒ~|{}{xrus£¹ÆÎÑØ×ÕÓÏÎÌÍÍÎÏÏÑÒÓÐÐÏÒÑÒÒÐÊÉÆÂ¿½¼¸±­ž“wgK:.-'//.279<>BKNULSNJIKKCH>>C=<7:4675-2/17CDCBCBDL]dp~‰Ž•›¢¥¨ª­ª¨«¬­¤«¬¯ª®°«©¤¢ ˜œ¬ËÉ£jNC=R]J>5B…©o2)..-((,4KfXN9326II3889:@njN>;59706IDKi€ŠuYa€ŽuTSTVUap†‘Ÿœ„UMCHhnVS\e{‰‰nGJFc`Rs˜™wwy²P.,/716?>DCKXN<94+-.8FW@?4.8<60-1,66:?21.294557430189@<:79,+5E=.5596CxkUGA>L@87>59Gq„†~eR\ƒ„x{xz~xšœ¦‘\HROOUwoXa[j€’WC>^‘uv¡wvWX¦¬‚I:+,266<:@JSE3+'**+:L\>5/.3:42323,39<42/58663.49426<<;:9;9;;5499/34EHRY\diuow||~{~~{}y{|}ƒ„…„†‡‰‡Œ‹‰‹‹ŒŽŽ“”‘“Š‹Œ‘‘Ž’–˜—ž¥«¬«±±º»¾ÃÂÅÅÇÈÇÆÊÉÈÌÊËÊËÌÊòœz]?411*05+&*2**'137?QYK_w[FLLXPTJJQDR^SPGBOZK7J†¶£–—“…z‚€‹…„ƒ†‹„€z~{x{tqrrginlgimmdkkgjifmkrqnwvsrptntrsxvtsxxponmps{¤»ÈÍÏÕÒÑÏËÌÌÎËÎÏÏÒÑÒÓÐÏÑÒÒÑÎÍÅľ¹µ¯¥–…q`I5%+&,.07?=?AHIMQWWOXP[\oocgXWJBFB>6641.154BD@B;4:=BKVis~ƒ”™£¥¨«¬©¨­ªª¬«©©¨°°®«©ª¬±Â¿š{fXF>AALX?`Œ§6'!'$*-&"+1FWOJ/+9>;-0408HkqiQ:;EC<>>)*0Nˆƒs\[jtov‰‰ŽŽ–žk?BSc]L[sl`adt{xdPMd”˜”·Ÿ…}S5y·ªp=)+*0:64AIWH;.1,3.7TU=62/:81,2+0-6;6755-03.4/730+06>>?9<9@=73=9-57FKUV\^iwurzz{w|€€€~{}}{}}„„„ŠƒŠŠŠˆ‰ŠŽŠŠŽŽŠŒŽ‰ŠŽ‘‘˜˜˜¡£¬««´´¸»¿ÁÅÅÅÅÉÊÆÊÊÇÈÊËÈÌËÌÌÎ˶šx`A0.12-*&$&($*5:CU[IXuZGQM]LWJNUCV\QRDCEFD6Mе¡••–ƒ~~†ƒ„…ŒŠ†|~}wxwyzusuqkjijlifgbag^gjjiinthliiinnpkopjmoomhggk£¹ÄÎÏÔÑÒÎÍÈÍÌÌÐÐÐÐÒÑÒÑÐÐÑÐÑÐÌÈÁ»·°«”|dQA47/**+1:99<@FLPSUYWTQSYYyytqcc]RQKI?A984487@<48<602;AJ\mvˆ‘™ž£¨¨«§¨«­§ª­©¬ªª±²±¶¿º²«˜sgVNBGB`bUˆ—ƒV1+$0.1('(--9MUU@0GT+%--6.;YkfT=IJ?8MQ,"$F8;:@4479;124>OIYWbmvyzzzqz~€}}{~}}|~|„ŒŠŠ„‡…‡‹‡‹ŒŽŽ‹ˆŽŒ‹’”™š¡¥¨¬¬µ¹º¼¾¿ÄÅÃÇÇÇÇÇÉÇÊÌËËÊÈÌÍÏÑÎdz—yW92/)'$(&$$)62BQNKRsaDJOZMVGPPAQ\MI@JK@I5Sг•˜–“„}…ƒ‰‹‡„„…Œ‘†€‚‚€}~wv~ttormkjkhibfcefhbdZaclfmedefeiiehmkqidbae_a‚ µÀÉÎÑÑÑÍÎÌÉËÏÏÐÐÍÐÐÏÑÐÑÑÓÑÏÌÄÀ¶²¨™yaF3/-+/027>C=@AEDJTRRRQRUVU[~yrohYVOOLJ?;856179DO\gs~Š•œ ¥¨©ª¬«­­ª«ªª­­¬±¹½É´ž˜‚vh[KDHU{]ލ^>/)(*#)')+/-4=H]PJYS$&+416513061/-1,48::?<=6><=>A999.-19NQT[^jov}yt{{z~{~}}€y…ƒ‹ˆŠ†ˆ‰†‡‹ŠŽŒŒ‘ŒŠ‹ŒŒ‹‹ŽŽ•“™™ž¢©ª¬­´¸¾¾¿ÁÁÄÁÆÇÅÆÉÊÉÉÉÍËÊÉËÌÍÌÐÑÐÈ´“jH2)&&*$#$(22COI]Ul_AGOXOYOLOCPUFIAOC9P1[‹³ššŒ“‰~‰‹Œˆ†‹‰ŠŒ…‚†~ƒ‚~{ytuzsrmkbhifecddga`_aeca_e]cdadnnlnnd]ZXTYw—¬ÀÉÐÑÑÏËÍÌËÎÎÏÑÏÐÍÓÐÏÎÑÎÍÌËÊÅ»²©˜„`E4.,3226=;EBFCCILIVWURRUWWYY€€€ƒ€zqj_YUTQAE:=@GE@:58403;BLVip}ˆ—¡ª«©ªª®¬­¬®««­­¶½Ã½«¤ šŠ‚xm[OBOour¶¬`>.*$%'))'&&-/,.AXbaqM #1199MulbUMKBQkC%%!(8Lƒ…„|xhVJLTbKQ|Œx8Aepa`cZcgfc|puotplps_–«ˆRBWJQNKi•d3/4:51?L[@5/.'%,:P_:1-.821--51//6=788:2410.-1<8966>=7:EI@74<82/25GMQ\\klpstu{~~zy~}x|~€|‚ƒ„†ˆ…ЅЇ†‰‰‰ŽŒ‰Ž“‘Š‹‹Œ’””’šž£©®°µ¸»»¿À¾ÁÁÂÃÄÄÄÊÊËÉÈËÊÌÉÊÊÎËÍÐÑÒÑÄ©‰]71+%%&&%14HJJ\RlfCOKYJUJKLARUIIEHJAQ1]‘±–••“‰‚’ŽŒŒ‰‰‰‹ŽŠ‰„ˆˆ…„„~„y}yx{onqkiojjbecabaa]c`^cY]`d^hnkrngd]VNMYk­ÀÉÏÑÑÎÎÍÌËÎÏÑÒÒÑÐÏÐÐÎÏÎÌÉÈÄÀ¶§œ‰iL-'-+.5A>ABGEDGKNPOVTSSRYUSST……„„ƒ}xla\VWOGBC?NK;?8926/7BFVgr~‰’—¢§¨««¨©¬¯¬«­©¬¶´´´±¥¢ —‹…yj[QQa‰hÍ¢;2.%'.&(((&*0)**4M_wwG!&*29DvxjSIOLbrA%"#$*9Ru“„†weYk]@Z|Ži?3TwggditlTZ†}nsnpw‚k†¤¢~ZiD5S_P“W37648DFSB310,'+8RO55-+54/0.0-(,487=62343..2059;:6>=>?LJA87862127>FQ_bggstrwzx||z{zx~€}€ƒ€}‡ˆƒ‡…‹‰‡‹‰ŠŽ‹‰ŠŽ‰‰Š”––˜š ¡£§¨¬²¹·º¿¾¾¿¿ÁÃÀÁÃÅÇÆÇÊÈËÌÎÌÊÌÐËÐÏÓÔÓÏ¿™oJ,+)!$%85NGHWRh`FHJVGOFHJ9<99:1:522546:738>ECHMC=6?<135:AKTefikotnnvsy|y~~€…†~„†‰‡†‰‹ˆ‹ŽŒ‹ŠŽŒŒ‡Š‹•””—œ¡›¢¨¨¬­±¶¶»¾¾½Á½½½¾¿ÀÅÈÃÅÆÉÈÊÊËÉÊÌÍÎÏÑÓÕÒÑÆ¬ˆT3,'$&59LF8WWffDAIQFSIDI@JRAHLQE8H6f–¦ ”Їˆš‘“ŠŒŽ‘ˆˆ††‡ƒ‡…†„‚„}}{}{wsqppmnehbbd[^``YZZZ`cpv{tyqj_TLGRd­ÃÊÏÐÎÎÎÌÊÎÏÒÓÔÒÏÏÎÐÏÍÍÊÁ¿¸²ª›…rTB3-.8@DKONOPJGMNISSTYUS]SZZ[YWŠŠ…„Œ†…‚}yvj_]UQQRSMID=;/736?LXgn|‡’š ¢§§©ª¨©©©¯­«ª©­¯¬¨ª§¤œ“†zn^^l eaÀÁ](+3)'(/).*,+((6@=IG~tB& $#$2:K‚ŠsGINavc7-7+'(4IQXVMk†ycc•ŽEBPA=R~kpnylLR‡ž‘yvƒ‡ˆxlilš›g5//Y„c›¡W3/7:GHN43+.($)=cW?83.7<<215--,5>83:OG232205451:5::=;AFLC8A99508>OPY_cmonpvvu{zy€|~|„‚~x€ƒˆŠ…Ž‹Œ‘ŒŒŒŒŒ‰Œ‘““Š’“––œœž¢¦­­¯²¶¶º»¼¼¿¾½½¾ÁÁÄÂÄÄÁÊÇËÈËËÎÊËÍÏÑÒÒÒÔÔ̺‘c:."&+-><0A\ecA?JKLQAIMDMTKGFK>:C:r ¥š—މƒ‰•““”ŽŒ‘‰‹†Šˆˆ‰‰ˆ…††‰‡€{€{x~yrurtnjnhg\fc\VYV`_jvyzwvqfYOLLc°ÁÌÏÒÐÎÏÎÎÑÒÔÔÓÐÐÐÏÐÐÏËÆ¼±«£‘ƒ`P;.-31@FHLNVPJPMRSSUUXYZ\YRSYXTUŒŒˆŠŒ‡‡…„yvng`ZURTSOFG82/+/@HTho}‡‘𣣧§§ª¨¬¦¬«ªª¬¬¯«®¬ª¦£–tozp¡UlÀ;.-'&,+(.,'**-%Aic]Xvi@'!#*Aq‰ŒcBIUneD.2:/#+DeY]C9FWumjyš„aMBASUE>]‚zrvpubVn“£‡uƒˆ{_eq~¡–b3)3Oem£…C299EK?37,.(./9_ID11388663:/1.1596:FE43735,275626>>;FLSL:889317DTQXcejwvwrsttzx}z€|ƒ€|z{„‡…‡Œ‰Ž‘‹‰†Œ†‹Ž†ˆ‹‹’’“—››œ ¦®­®±¶¶¸¹»¿¿½À½¼¼ÂÁÅÄÁÃÁÇÇÉÉÊÊÌÌÌÏÏÎÐÒÑÒÕÔÐßsB1.1.;:49]c_G@LMNPHDQI[TJ@HL?4F=t¨ ”‹‡„—‘’‘Ž““‘Ž‹†Œˆ†‰†‰†‹†‰ƒ…€xzyz~upprihflle]ZVYhjir‚ƒ~}sljVIFQi—±ÄÊÐÒÐÒÎÐÍÑÓÖÕÕÓÒÑÐÐÎÌʱ©›‰yY@//*26>EKOQPJPOTNRRVWY_Wb^YUTSWQV‡‡ƒ†ŒŒˆ…ƒ„{woscb^`[SOF>0,+2@KUeqyˆ˜ž£¤©¨§««©©ª¬­­¬¬«®­©£¡œ•‹}y‡’~³Ž`t›t./,+)0)(&$')!$,6k~|u|mXD.-+3KpˆtG;HZYJ=@6--*53-.-+0=ZOH:23;4382613151=77@KD5374:44;>=4BA8?MQQE<93847:QO`[finttsywwz{~~zz‚„†‡Š‹ŠŒ”‹Œ†ŒŠˆ‡Œ“’‘““•—›žŸ¤¤©¬±¯±µ¸¶¼¿¿¼¼¿¼¿¾ÁÄÂÄÃÁÈÅÉÊÈÌÎËÍÎÌÐÐÐÒÑÓÓÖÔÈ«zI.-68009WfaG>NGGPBAQEPSCDEF56F;{ª˜•‰ŠŽ“›œ”’‘“’‘‹ŒŒ‡Œ‰Œ‹‰‡‹…ˆ…„|‚|{y{yx{sunooorfdfZbcltx€‡‚}robTNEVu›¸ÅÊÑÑÒÒÒÏÐÒÓÖÖÔÓÒÒÑÏÏÍÉ¿¬›‡lV<0',)3=MPPUVSKOJOOXYY[[_[^]]SVNRW[ˆˆŒ‹Œ‹ŽŽ††}tqja_bYSPK<0,*2:DTesz‡“•Ÿ£¦¨©¬ªª¬¨ª¬­«±°®¬¬©¤žš‘‹€w”–˜Åm`|zZ4'.-.,,-'&'$ $%*FCQDFPY0'-:_xvO<;87674236>@=>D97JOOH746=82=XR[cdqqustxxutx‚~{|~z„‚‡††‹ŒŒ‘‘ŽŠ†‡ˆŒ‹†Œ’’‘˜šž¢¢¤¦¨ª­®³µ¶»½º»»¼»¾¿¾¿ÀÂÃÁÃÇÆÈÌÊÊËËÌÎÐÏÐÎÒÒÔÕÔÔʱ€L0.7/+.N\XA=FDEF<@E?NRHEEH24H<ƒ¯•š‘ŒŠ‡–Ÿ’—”•’’’Œ‘‹‹ŠŽŒ‰‹ˆ‰ƒˆ‰…‚†‚~€yw{wvwusljkhg`[_rn~…ˆuo`XQNa…Ÿ½ÈÍÐÔÒÒÑÓÔÒÓÔÕÕÔÐÑÑÐÎÎǹ£pS;@'&(4;MZSTWSOKNMMNRZZ\[_d]WVVVNQTV††„ŒŠ–’—’‹†„ztfjfZYKDA6/,56BWfs€Œ“–£¦©§««««­©¬¯«®«¯¯°ª§¥¡—‘Š‚‚§«¾¹T_ntY9,+,*+/2,"')(!(.@Zjp„ŽŒƒ€‚‡‘ƒdEFMQ>EBHWC),=g}^@9;>IfŽzbH`O>:60<55:.52//81999B99>85432.2„«“š”†ŒŽ–˜“™‘””‘””‘‘‘ŽŠ‹‹Œˆ‡‰ŠˆŠ…†€ƒƒ‚}xwwvytqtqmkeeemwx‚†Š}xphYSUjŽ©¿ËÐÐÒÒÔÑÔÓÑÓÕÕÔÑÒÐÎÍÎɳ˜€ZD0+6.2=KT^ZXVNNNTLOSY]a]cfa``_TTSTX[‚‚€ŠŒ‘•™”“Œ…|spj`ZRJD71*-6EVcv}Œ˜£¤«§­­®¬­«¬««­¬®°®­¨£ —‹„—ÂÇÌžFt`pUD981''()*()/,&+'2=CD-.2@Jz‘VM:9EE7<:>Nfc_LLazs~~‡“†ˆ‚–ƒ—n3*&7‹¯œa.+A\^¯–B/?;81.(1*0/5^KDB10@646762+-1.4A=@<=:984233/;9IJTI@Q?E,/6Fˆ¨–”ˆ…•–‘•–““‘•‘ŽŠ‘Œ‘’Љˆ‹Š‹……Œ‡…€ƒ~€{}zzytvyn~kginru€„‡‰xleZV^z˜±ÅÍÐÕÕÓÓÓÒÔÔÔÒÕÔÓÐÐËÌÉÆ¹¤xU:,**;:OZ\[^ZZWUQRNQ[a_d`ecba_[UOSP[^~~‚ŒŒŒ“–™••Ž„zvpdZQGC<0)'3GUgw~‡’˜¡§¬¯®°¯­«©²«©®­²´¯­ª£¤—ŒŠŒ¶ÖÛÀnDfEXNL@;0,.%/*&'*('/-4?H<'(-7IUZMN@=:CFK<8=DOVjpylL.,'*;c‹€;89;;MF99:@ZegYW\alr}†ƒ„Š…|œ”£‡K$!&k§²˜N*4UP˜¶\62588/-.(**6XO@:24632:=7825>52>?F=A3/2957569N5?C??UV^cI=20/*7WSWZpsqsqvy€„~|~rnv{~‡ˆ‹‹ŠŠ†‡ŠŠŽŽŽŠŒ‰†‡‡‰ŠŒ‡Ž‘”–œœ  ¢¢¥§¨§ª­®³³³¶·¸¹º¹¼½¼¸À½¿ÃÅÆÆÇÃÄÉÉÊÌÍÏÐÎÐÓÓÔÓÕÖÔ××Òº‰U1)6APTB2>9B77@JGPC?K;D).5L¨•”…„š••’˜”‘’‘’ŽŒ‘Œ‹‡†‰††‡††…€„{|~xw~wzwutnkmqz~„„wpj`Zcˆ¤ºÇÌÑÔÒÕÖÖÕÔÓÓÕÖÖÑÒËÊÊÈ¿²¡‚gG0%+17FP[[^ZWSVYVQQS\aagdoe`\\Y\ZSW]`||……Š‘‘“˜š—“•“†ƒztkcVGB;8+'6IUfp|†“¢¥©­¯«®¯¯«®®«¬­°²¯ª¦§ ™ŽŒÄ×Þ¬cZdGY::>@82/,-1)/)&*04CQR9 %%3FNC>B@C:<38AV>A=?LejgSV]Xdqs†„„Œƒ„£—„¤Ÿg1#%<б¼…:4`Wb¸€686/1-'2,/-9UP;:46<5161:26333.<8D?A7,253:27;R@L;@GRY^`YB9-021MVJ_ostsrvyz„ƒtqqtx„…‰ŠŠŒˆ‹ˆ„†‹ŠŒˆ‰Ž†ŠŠŠŽ‹Ž““–š™™ž ¤Ÿ ¤¦¨©¬­±°³µ´´µº¹¹½º»¾½¼ÀÀÃÄÃÅÄÆËÉËÍÎÐÎÌÑÑÐÒÔÖÕØØ×Òº†M55FNXCAE@D??JOPUJLK?I.7:V”­’›˜‡‰–’˜“—•‘”’—‘Ž—‘’’ŽŒ‹ŽŠ‡‰‡Š‹Š†‚„ƒzyzyxzspptu|ƒ„‚†vrnfbnª½ÈÌÒÔÔÕÓÒÕÔÔÖÕÔÖÒÎËÉÿ¸¬”}X;,%-6GNU[^WYYUTRXSU]bhfhefi`\[Z^TV_efƒ‚†‘—™—•—•’ˆ„|wi^UH@7.,7KTbn|†“𠤦¬®­¯®¯¬®®¯®°±±°­§¤—•˜ÇÖÙ–qx€iV-1+662145@637:/;@AJN7-,-8LYLGBB:<5400=?BFIC@;@:4$%%=bˆŸs@/0F?@XIED=@LgshVXR\gp„ŽŠ„{ˆœ€Œ¥}J((,^¥À³c>qUL¥ŸG/2.)*+0-*25VHD34396/74865-477<>EBD37315859;S;EC9OO^cgfK61,-+CLNSpvqxtww~~zqprx€†ƒ‰ˆŠ’‹‰‡‡ŠŠ‹Ž‰‹‹’’””˜–›š   ¤£¦¥©¥¬ª¯²²¯´²²¸µ¶·»º½¾½Á¾ÃÁÁÁÂÅÇÉÊÊÊÍÍÎÑÒÑÑÕÕÓÔÖ×ÖØÑµvD-9AP?0;4<57HORREJDEG(38]ž©“””…ˆ•””–š•’‘Ž•“ŒŽŒ‹ŒŒŠ‰‡Žˆˆ†ƒ‚‚„}}„‡wwuvrtx|~}€„{upfhz™±ÁËÏÒÔÓÔÒÓÕÔÖÕÓÕÓÑÌËÇú³¤’pQ:/,2?KUZ\U^bZ]YYQWV_`akljib_^YY]YT^e^z|€ˆŽ”˜˜™˜˜–“ˆ…††}qbWMA8*+9ASbq‡™ £§¬®±²°­­¯¬®°¬´¯«®¦ ¢—–ÏÛчy‘‹¡_%#)7&+14:=;<655D=BLG?4-+9X`QC>89?8?@B468;14,/>22/)-:V‰ŽrP3)8:DRQMHHADUomZ[PU\iŒ’ˆŒšŠ]“‡sG58I›¢È—XyU>ƒ¸k8:58/24538;XIC8>7<6;<<<959;;<@?ECI867384::@[HG@9KUbhieY?61.-?TN[rztzwvv~zzsgoy…Šƒ„‡…Љ‹‰Š‰‰Š‹ŽŠ‹ŠŒ‰‹Ž‘‘“””–™šœ¡£¤¢Ÿ£©ª¦¨¬§¬°¯°²´¸±²µ¸»À¼½½½ÀÁÁÂÃÃÈÆÌÉÊÌÏÏÑÐÒÒÔÕÔÒÕ×ÙÚÙϳp;87HA/14>74@DOF;MJNG)08a¤§Ž•‡‰“–“’””™“’“‘’‹Žˆ‘“ŽŽŽŽ‹ŒŽ‹‹‹ŠŒŠŒˆ‡Šƒ„„€„€€{xyztxw}|„…‚€~qon…Ÿ¶ÄËÑÕÓÓÕÔÔÓÔÕÓ×ÖÕÐÎÊý´§š…gH5509FVZY[W]\_a\ZWUXdijunjih^_]WVSW_`_yyu~~„’“–˜˜™–•‰ˆ†ym[YF2-->IQ^n~‰–›¡¦¨®°²¯­­¬®­¬®°±±«ª¦¦Ÿ—•Ž•ÇÜÇu\tŽÆi$%!'#%0188<<<;9AI?PLG7/':RH@<833:BA@D@6=-++/20>C9//9Npkph=)-8GRJLSHOKN`ygeeSUgv…“…‹|‹‰_u‡…n]TE‰‡±®s`B9[¸Œ@3.,,-1743=bKE;=9F5>6=:839478<HNO<)1;i«£‘’ˆ‹Œ“•›–‘“’’•’ŽŒ‘’’ŠŽŠ‰‡‰ˆ‹†‹‡‚‚‚‚{~yvyxuvzz‚†~ƒ{upp‰¨¼ÉÎÓÓÓÔÕÔÖÕÔÕÖÔÕÔÐÌÅÀº¯ Ž|^B418DO\a_\[[___[ZYZfkols€nje`c]VQT]`a`ggkp{ƒˆŒ’›˜™“‘•’ˆ€pePC4+/6HXep}†“˜ž¤¬°°¯±±­²®°¯­¯µ±®¬¨£¡œ”ŒŽ®Ð¿gNMÅO$)$-).-,4361,41AMGGABA41568;4,14GU\IFA972$+++-?L6.,44GRgu[5/3;GD>GFUIIKovf`VO]vˆŽ„Š…€~di‚Šuncyy{—‹\02@©¯W.0220.4,2:^G?B=8G467152,./35<:889/12.48BFCC?K\ivvgdO6)0+6QLWpzv~trqln|€ƒ€ƒ€‰†ˆŒŽŠ‹ˆ‰’Š‹‹ˆ’ˆˆŽ’Œ‰ŒŽ–“•—›˜›Ÿ¡¡Ÿž¢¤¥¢§¦§­°¯°®¯±±¯¹µ³·º¶·¸»½ÂÂÆÄÄÇÈÈËÈÍÎÍÐÒÐÑÕÓÓÖÖØÙÙÙÛØÉ‘G1:0*'-32?KB<ILcvteWQr‹—‹€€…ŒˆsŠ‘l;i}P8dðU.4qÓ¡7$(&$-(&.<-&0Aƒ¯ŸŒŽŽ‹‘‘”–”–•‘”‘“”‘Ž’’ŽŒ‹‘‰Œ‹‹ŒˆŠƒ„ˆŠƒ„†…~~y}~}|ƒ‡ŠŠ‡‡{~†¨¾ÈÎÓÕÖÖÓÔÔÖÖÕÔÔÔÓÑÍÊ¿¶¡—ƒoZJCALNVac`ZYX\[[\]adjpnjonnjd_^^ZTZabghh<&&0IŒ®›‰‰Œˆ–”•’•“”’‘‘Ž“‘ŽŒ‘ŒŒŽŽ‹Š‰‹†…‡ƒ†‰‚~ƒ€ƒ}~}}€……ˆŽˆˆ~}|Œ¯¿ÉËÕר×ÔÔÕÖÔÕÓÕÓÒÏÌÉÀ²œŒ{bTFGEM[_cc]T[]_b_adcdjmtpomjdeb[_Y_`cbggh338O`iv|†‡’›£§£¥¤¡™“‰ƒwgU6*'5BS]m‹“Ÿ§ª±²³²¯²¯¯±±±²´³®¯«£™ Ž¿Åz}‚MT?,(*'*(3++++6,..;_O4/0;<5851,,/)0J_nkZ4,OSF.'(+'3RvU2(0*1S]LTZeC18AOMLKIGUC[th^Xywzsvpwt•Ž}ޤ§|‹j>+9d®Z;UÐÎf,(%&+&+.7YB=@6:D678683,265:5>AD;5<<65/+/7II>?ETiozxsi`I3/+1PTVbshsgu€…І„~„‡ˆŠˆ‰ŠŠ†Ž‰Š‹Š‹‹ŒŽ’‘•–™˜™˜œ¡¥ž¢ £¢¥¥¥§«®¯ª­°²µ·³°¶¹¾¹º¼¿¿½ÁÄÅÅÁÅÉÌÌÌÌÏÐÑÓÑÔÕÖÖ××ÚÜÞÝÛЛT60-,01D77>8433).2BJ;5MXou|vqkcK5-).JNVdl`unz}€‡‡ƒ„‚ƒ†‡†‡ŠŒŽŽŽŒŒŠˆŒŠŒ‹Ž‘Œ•‘–˜•••—˜œŸ ¡ ¤¢¦§§¨«®¬®²¬²±µ²²µ¶½¹º»¿¿¼ÂÃÃÅÄÅÉÉËÍÊÌÎÏÒÒÑÒÖÖØÛÚÚÜÞÜØÁ€J,(*030<5/.5@@%*5`𤓉‚‰’Š”“‘“˜‘’’”‘Œ‘ŽŽŒ‘ŠŒŠŠ‹ŒŠˆ‡Š‹†……„{{~€ƒz|ƒ…ˆ…‰ˆ‡‰‹‚‡œ´ÂÎÒ×ÙÙ×ÖÕÖ××ÓÓÕÕÒÍÈÁ¹¤Œu`PNNSPUZe`_W]cbdbddhmquvwnib]X]]_\aaijiiih%%.9O]nsŒ—¢¤¢¥¨©¤ž”‘„}pW;*#0AR]q—ŸŸ¦­¬®±´¶³²²²°³³´¯­®¨££–‘§×žocF[c/$-%(/.*++17/.1Tg6124..E=372/**3Qi~lK2*=X@;+$)%'=anU<-'*;jO@9RnaF2C^e_SSG@;Y[c_u€\Skvq†wy£¨° ”uQ+&&)P ‘NO·ÓŠ($" # %$7P>./.A;77O`hvx~qqkQ2&)(OISfck{v~|ƒ‡‰………Š…†‚‰‰‰‰‡‹‰ŽŽŽŽŽŽŽ‰ŒŒ‹Žš”•™˜™˜œ›ž Ÿ¡¢¢¡£¥§¨¬¨¨°¬¯­±²°´¶¶·»»½¿À¾ÂÄÄÆÈÇÇÉÌÌÌÌÎÑÏÏÑÓÖÖÙØÙÙÝÞÝÜÓ´f1&'/-2=;.-3F5%&9aŸ¢ŽŒ‚‹Œ–’—“Ž””‘‘Ž“ŒŽŽ‹’’Œ‹Œ‹‹ŠŒŒ‰ˆ‡‰…†…ˆ‚~…‚€Š}}„ˆ†‰ˆ‹ŠŠ‡‹§·ÇÌÓ×ÙØÕÓÔÕ××ÓÓÓÒÑÏÉ¿³Ÿ…jVIIPPTT[`^WX^cca_cfjorrsola^ZWa[_\bedhhldf&&*4CScnw†˜¤¥Ÿ¦«§¨ ™†‚r^>-(/:K`s‚—¡§¬°±±±¯³²³±®¯±´µ³®¨§Ÿœ•‘‰Ž»ŽƒsI{(& +*1&((60%,FfR3+*31*2@092220/Us~qR,+:N>;'%(#!,JbbC3&.EJ_]p€^Rm‚„€qlu™—®¦•{P,$#%3cˆiO¹ÚŸ0$%&%$'+3P<:8/ED23.47/+-514;DQRA/4;8?=++8=:=:R`psvxulaT8.('$%(.8l:(.7B3$&7l£œŽ‡ƒŽ‰’—”˜“‘’‘‘’““Ž’‘–’’Ž‰ŽŒ‰‰ŒŽ†‹‰Œ‰ˆ‹‰‡„………€}ƒ|y~y„‚‡ŒŽ‘‘’˜­ºÇÍÔ×ÙÙÕÔÖÖÖÕÓÓÑÑÑÍȽ­—t]LKKOXV^`_YVWafedbeflrtnpi`b[X^Z]^egiiikijd##',4BOer‰– ©¥©«©«¢’Œƒ|_D)'-2Mar™ §«¯±²¯²¯¯±±¶µ³²°²¬ª¤¥”ˆ€†qK“x2"$#.5+0(+31-4[RB5,+*252612.4-/4LnƒsL)&4K;H.'+$"*9F`bJ1%/aVE:8KdhYOLffefe\L@DS`tx]Rq…‚jh‚Ž‘¶¦xd8' +H‰}NºÜ²5"3$)$)%:OE><4GD2,24/,-*33467KZ:.<792<-.7?9NO264/5-/364.29JT:2/869/*07;7;N\hsqoqul]O.('%BJYhkupq€……}€‚ˆ…††ˆ„„‹Š‰Š‡ŠŒŠŒ‹‘‹‘“’‘Ž•’““˜–––›š¢  ž¥¤¦£¥¥©¨ª¬«®°±³µ¹¶´´·»½½¾½ÂÃÄÂÅÊÉÊÎÐÏÐÑÏÐÐÓÕÕØÕØÛÚÜÝÜÝÛÑšD:%-+32/*40'!(=¡—˜ˆ‚Œ˜™’•’••”’’‘’ŒŽ‰ŠŽŒ‹ŒŒŠ…ŒŠŠ‹Žˆ„ˆ„ˆˆ…„‚€|}}|z|x€‡‘œ¤ª°´¹ÁÅÊÏÓ×ÕØ×רÖÕÖÔÒÓÑÎʯšjXNPVX[`\ZW[\`dgkjlorrpni`_aYXSXZ^fgihgi_bhc%04,('+1-,'.0?39K?50---/42/4-+0*010Rt‡sD+./HTL?'(("'/$-JccZ=?gK8.;OM[[XL^stqvmnL?S[^WPhŽ“€y””z—¦Ž\>Esܱ׷?,,+) '(DUD4;EOO20464+/3094;8GB7*2./252-656M\hai`D-1NA;ViVJrslnfjbMSRZRIYv“–¡˜‡›š”<#(:TI4:kì·Ó­9" ")GN=06LXO132.61(3.=766DC63-04:F?6898KX`lmqjhfcc\?6'9064.5./,.2:,91.*,3Mlq6$&+:TN21(()252)876@OUV[J:2YO6H[cW`sji[^SPQ[]OP\v’˜–—Œ—›¡‘S'$-[O12j¼´¶Ð .$)$&"$)KFJ14OhY1+08.**42-*67N>34./09024<8=T^jsroja^dmdD9',8S_Xtxwv~ƒƒ…ƒ}„„†‡Š‡‹‹‰‹ˆ‡ˆŒˆ‹Š‹Œ““–“—Ž“”š’–—˜œžœœœž¡¡Ÿ¢¥£¨¨²ª«¯®²±±¯³¶½¸·¿¼»¾½ÂÆÅÇÈÆËÍÎÎÒÐÐÑÒÖÕÔÕÔ×ÙÚÛÛÝÝÞàÜÏŒ9)(,'%$'+ +[ž”™œŠ”—˜™‘“—˜™‘‘–“‘’ŽŽ‹Žˆ‡‡Š‡‡††‡‡‡…‡†…†…„~z|yyyŠ˜¡«²½ÃÄÅÆÇÇÌÑÓÕרÙÖØÖÖÖÖÓÐÑÍÉ´¤Šo]XY][[^f_\_`fjefllnpsokhbbZYX]YYbaelegdd]fifg('+26Q`z˜¡ª­«­®¬¥ œ†mP/'%&/H[o…ޕ𣤫¯³¶µ´±´³²³²´¶±¯®®«¤¥—‘ƒtxhz²i/5132//-.2:?@:.47204.24-01/-32(6O}–‰l6&&'.KYB5#$(?:XjZVfqeQcUGR]YLVbv’”•••y¡¦j( 1C5-1g»»¶Å/ #)(*%)MPN/9Hg]3/885.1<+/.;$(0G?1&5>D8)#4=2.08dP5Neg`Vlk[X[KLYQQRbq„—š…ms¯¥‚wM*,.,09[¸¾¸¹r*("(,+.8WSV37G_O;85:5557641=;C<6.,*-01-9:7LWajkb`_nstt_C/1*?`\cfqx|€„ƒƒƒ~†‰‡‹‰‰‰‹ŒŠ‹‡…†Š‹‹ŽŒŒ‡Ž““”Ž’•–™œ››™Ÿžœž ¦¨¨©¦¨ªª­¯¯°²²·¹¸»·¸»½½¾ÁÄÃÈÇÉÌÍÎÐÐÑÐÐÔÕÓÔÓ×רÛÜÜÜÜÞàÛˈ1$'!$%(!#3l‘’Ÿ•Ž„““”••—–˜“’’‘–“–Ž”‹ŽŠ†‹‹‰„‡…†„Š…ˆ‚€ƒ~€z{{}y~ƒ’¢­¹¿ÄÅÉÇÈÉËÍÏÓ××ÙÙØÙ×Ö×ÖÔÔÒÍɾ´¢‹scZ\V`Z[_][]dcjkllnppurlid_[^[\^]]ibjpcg``^keli!!!&.6@H`uŒ—œ£¬«®­©¥™“rQ5" ,@Zn‚’𢍫°´²²±°°±³¬¶µ··¶²®­¦ “•³zq61,4:/%(-0:A=11/.6+++&-,4510-2+3@l’’}c8#$%(@WM9+'2KA;16=B9+$*27>64YX=BdleYrvdXd\CRH=VeoŒˆŠlq†¨Ÿkrn1&/#+1T¸ººšU*&!$%)9WUW);JUM//9;47802227AH=@3-+0-1;;5=R[\c]ddgv~ztcH6/21+'$.39:*+,-&3M|•ŽvY6$-(*2@D9*!9MA3+*?J>,('2;007N_>:SgY`jxsab\XOH8Xi{’~kƒž¡žkg€M.1++1T·¶­n=4:("#'+3VRG+:MQK009<00,-33.7@AޓޒЛ“’—“–••”•“”’“”•“”Ž’Š‹Œ‡‡ˆ……‡‡†‡‡~€€‚~}xx|†Ÿ³ÂÉÏÒÕÕÒÑÍÍËÊÏÑÕÙÚ×ØØØØÖÖÕÐËźª˜‡o`^W\^_][ZYaeeeekqrpptrrfe_YYZ\_abikgjihda^ehifb !%-=Wtˆ˜™ž§¨®¬«¦•†pS8!#%0?Ri}Š˜™¡§ª­¯µ²³±²´²¯´±·µ¶°¯©¦ š—…–¬‚hHF+.:0&01=D;136*'3*,-*(-85&$%.-UˆŽnZ=02-035A2-)FU7755421@4(58,/,8OK=VbCYr{vlNDKEF:?Qu¤˜†–˜yYs¤žw]P>Gжœƒ=q¢/&%KXOL2ELA?23961>r—…zˆmb<$*$'(/657*EL63;7'.27.-6?-+11GF7H`BLnˆyt\:69<7=L¡‹‰ˆšš˜h4BŒšŠqwny¨­—qWšÂ¨D%&,+@PJG/?G<>84<32B?-360276443;:2;6:/2F@/582%*-0+1::33/)ACA=FA8d}†d<499@GV‰›‹‹ŠŽ˜™ }Q5zƒ>Vzœ³«œ‚‹²È¨F##$(FM=?1>F8DA:919BC4./52:10405>6098;NVjqzv}|{}xvvcR.+%$*7Pmƒ‰‘¢¥¯³·³´³²´²³µ³´¶µ¶¯®¬¥¡“‡€™}<1(.2479D?8))+9%,)3,-/*2-/"(%,8S~uNswS?4$,(',)/6:EM='+:3!'&(/*7G78*&>J>B524Fm‡†nE.BFUO^ˆŒ‹’–œ¡•wJTŽD;qœ§ŸœŠ•­¬{0'.).FM>50;<3=D=<45AF7216734.372?/B?nqB82).(*/.+9H;<)#=T@8GE4BgyqW=@Ngab‹Šˆ‡‹–œ¡™’Šx`|‹[c ©›uMQ^M;(,/)5GJ<6.?=1ND@321><4:6?>2,15.2.4IA>QSer{yw{}z|{|p[E3,CZZ`jn|w{|‚„ƒ‡ƒˆ†ˆ‰ˆ‹Œ‹ŠŠ‹Š‰…†‰ˆ‡†ŒŒ‹Œ†ŽŽŒŒ‘•”—“–šœœœž  £¡ž¦§¨¦ª®°¯«®³±·¶¸¸¹¼¾¾ÀÂÄÄÆÈÉÉÈÉÐÑÏÍÐÒÑÒÓÔÔÖÖÖØÙÚÚÜÞßÝÚÄt#" 4k‡‹Œ“‹Ÿœ•Ž•“’’‘“Ž’‹‹‹ŽŒˆ‹‹‹‰‰……ƒ„„„…‚~€€|{{w}{w}‡¥¾ËÑÕÙÜÛÙÕÓÒÑÑÓÕÔÖØÚØÙÙÖÖÒÐÊô£–…ufhf_c_]Y^W\`[bklpososnlc_\__Y^]aehjgiggkacebc^YWS $-+21.+))236FPF7'(3:*'/(0/7E:5' .SI54<B8>8E2.2?:617?:3251183..M…aHUJ0-)./38=KQH6/&88+&$$,1:DF5()O^;@8>]xniowzviqhr…Іˆ’uD?s‹Š”››Ÿ¸¿±Ÿ_9)#$ "4aC,7M=1/7=>0<8@9,7;;11/?:7921569CFAKMZqx{€|{{xzreP<0,O\[]fp}~€„€†ƒ„„†ˆ‰‹‹‹‹Ž‰‰ŠŠ‡‰‡‹ˆŽŠ‰‰‰‹Ž‘Ž“‘•’˜““›™š™› ¡Ÿ¤¢£¥¤¥¥«®¯®´°³´²µ¶¼¼¿ÁÀ¿ÄÃÅÈÇÉËÍÒÎÏÎÐÑÑÒÓÕÑÕÕÔ×Ö×ÚÚÜÜÜÚÓ®E'>€Œ„ŒŒ“—š–‘“‘’’‘““’ŒŠ‰Žˆ‹‹‹ŒŒ‡‹‰Šƒ‰ƒƒ†…€z}~{{|xxz~”±ÄÎÒÖÙÚØÖÓÏÐÐÎÒÕÖÙÙØÙØØÖÓÐËô§•Š|qmijkffd_]a`_banrwvuoikgf]_X[[_bfjkllggad^^]]\ZRRP"!"(6N]}Ž–ž¥«±®ª§ –Ž~oF-&$(3PdyŠ— ¡¨«²²±²²°²²°¶³²²²´±¬ª£¢œ˜’ˆyvkSC:;DG7,++++*/0.++..6.(-*8;;JYL/'*4^UPS<510,1/0?SPD8/(+60/+'+46CL<'#*SaH0:47KXP]ry‚{zq…†€˜oQ>Q€…|…—ž¯¸ªŠxW@)$'3ZU2)CH>43=N=+=:A@09?;2,5>;1/24215CFFPWdox||€z}||~{vmM;/.T]X`jnyƒ‚ƒ„…‰‚„„ˆŒŽ‹‰Œ‘‹ˆˆ‡ˆŽ‡‰‰Œ†ˆ‹ŠŠ‰‡Œ‹‹Š’–‘’•—œ˜šŸ›Ÿ¡¢¡¡¦¥¨ªª«­¯±°±±³´¸»»¾¾¿ÀÃÀÄÇÅÅÉÌÐÏÐÌÐÑÏÑÓÔÔØÔÕÖÕÖØÚÚÛÜÜÙÄj =}‡ƒ‡”“’‘™˜‘”•‘Ž‰ŽŒŒ‰‹ŒŽŽŒŠ‹†ˆ†„ƒƒ„„}}€€{~{x“¯ÃÍÓ×רÖÓÐÎÍÎÑÒÖÖÚÛÙÙÙØÕÓÌǼ¯œŽ‡ypojjjcjfbafbb`cmtqturoiji``\\__aahhldjhhbi[a_WVRTO" %(2Im|Š–¥«®ª«¦¢–}mW4''+>Nc~ˆ”™£§­°³®¯®°²®­­­¬¬±±­¦£Ÿž˜›’ŒuteK//0-,$/FX<:;1&)0*,*()59GB6("*NcV:6.*3==Bgm{‰ˆ}~‰›€o_YNa~y‡”š–“—¥˜€\I201260=KGKNXfoy}~€|{z{€{jH800RY^gjr|‰€…………†…‹‡†‰ŠŒŠ‰‰ˆ‰‰‡†Š‰‹‰‰‹Œ‹‹ˆ‹“”“˜‘“™˜ž—›ž›žŸ¡¡¢¢¦§«§©©¬­®±±°´µ»»¼¿Á¿½ÁÃÅÆÆÊÈÍÍÎÍÍÏÏÐÒÑÔÓÔÕÕÕÙØ×ÚÛÜÚßÜÍ…#!Gƒˆ‰Š‹•‘–œŸŒ’Œ‘”ŽŠ’‰‹Š‹‹ŠŽˆ‰Šˆ†‡…††‰„‚€„~~z{z~‡‚™·ÆÐÕÖÙÕÓÑÎÍÍÏÓÓÖØÚÛÙÚØÖÓÒͶ¤™†~xutlngffdaacacfekkqqpooid`]ZZ^^\dighffig\]a`[Y\TRQL!!"#"(/Ag€‡–œ§©¬­«§ ›‘}u[7)$/>Ocy†•™£ªª¯±¯¯ª¬ª¬««§©ª©¬«ª¢¡›˜™ŒŒƒzwG;-/90--1,00+-+.4641..9T_^>62/(,;9d[MZ[T8,8,&)4MR2@C6'*40*%.03>I1-4'.O^XA5///867Bax{Œ€r†•}{knVRfgo™Žy’©±¶¥yrˆr?5=RO9214B04@]LM[ZK.&814-:R[S=C1+,'4;AYx€Žy|p‰Žx†cmhF>27PgrYj£—Š•’ŸˆrlwvhS@9FH.7BH\[J9;325887/13//6HIIJR]ir~{zx~}}xw_F7+;T`bel{{}|~‚…„…„„…‰ˆ‰‹Œ‰Œˆ‰†„‚‡…†‡Œ‰‰ŠŒ‹Œ““”—–•š›—œžœ¡ ž£¡Ÿ¤¤¤¦ª«§§«°µ³¸¹¸¸¾¾¼ÀÁÀÅÇÉÇÊËÎËÎÍÏÏÏÑÓÓÙÓÕÕÖ×ÖרÙÚÛÛÚÖ¶W$]ƒ‰‹Œ•Œ’—“ž™’ŽŽŽ‘Ž†Š‹Œ‹ˆˆŒŒ‰ˆŠŒ‹‰ˆˆ‹†‰„ƒ€€~~~||}~€ƒŒ¶ÉÎÔר×ÓÑÎËËÍÒÓÔÙÚÙÝÖÖÕÎÉĵ§”ˆ~zwu{qhgh___cdcilifnnsknfac\_[[\Z^glhgbildbcibTQU\XQQ%"$4@[m‰” ©¨¬­«¦£›‘…u]4%",8K`r„“›¢¨­®±²´®®¬®«¬««¯±°®©§£Ÿš”ІŒˆ“q;9-*.5:62/01/>HKNTXenv{€y||‚€|zx]K>1FUcmgr{{}†††ˆ‡…€Œ†ŒŒŒŒˆ‹‰‹‰‰…‚ˆ…††‰‰ŒŒŠŽ‡Ž’”—™™”˜šœœ–Ÿ¡¢ ¡¢¥¦©ª©¨¥ª°µ±¸·¶¹¾½¾¾Â¿ÄÃÆÈÈÊËÊÎÎÍÎÎÐÒÑÔÔÕÓÕÙÖØØÙÛÙÙÛ×Çz" %_ƒŽ‰Œ—‘•“˜Ÿ’’ŽŽ‘’‹ŽŠ‹ŒˆŒŠ‹Š‹Š†Š„‚|x}|{€€‡‘£»ËÐÓÖÖÕÑÎÍÌËÎÏÒÖØÚØÚ×ÕÕÐɽ¬œŒ‚zwrwysjjkfafcb]cehlplmnmaZ]Y\[Y_b^mlhhicgb^]e_TTW]Y[X#%/JYqƒ™Ÿ¨©­®®©¤œ“‡uV0%&*;Kcx„”›¡©«±²±±°´¯¯²¯°®´³±°©©Ÿ ˜“‘š‡k6FO,*(*1-)9--.+,697-,11I0:/15:55+4]WVOF?75,.>BL8307C=+-7<+93.,2MC&.8WhrA$E,+74,(-;,.-4.431,,*012436>67.14NXTOGA5@7)AA44'/.D?.781-+#&(7A;1(9Uoi9%5I7 '85ASb}‰uUqŒ’n\k’ˆdbtP*!$*F|2$-.*MO(!"-LJZIDHy¨§~mmhF=/316?C42/,2CE6++(/*.#68E<8.1Yrf4!#>S-+5AE[_t‡rMeq]Xy’ƒ]v8%+.;{B#-*3W/! %/DCB55:OeX>IeƒiUN=43588=3)553GDKUVWdju||z€|ƒ€~~dOG+;MTg^lxƒ„…ƒ†……ˆˆˆŠŒŒŒŒŒŽŒŠŠŠŠŠ‹Žˆˆ‚„…‡†ˆ‰ˆŒ‡‹‹ŒŽ‘’‘‘–˜•š”–˜œŸŸœž ¢¨£¤§ª§®®°®±²µ³¹º½Â¿ÃÀÃÆÅÆÈËÊÍÎÏÏÎÎÏÒÒÓÔÓÕÖÖÖÖ×ÙØ×ØØÖ¾_5u„…‰’Œ’—”“” ŽŽ“‘‘ŽŒŒˆŠŠŒ‰‰‰‹‰Œ‰Ž‰Œ††‡Š‡€€|€}‚}€†Ž§¾ÅËÑÕÙ×ÑÎËÈÉÎÑÓØÚÜÛÖÔÑÎǼ­šŽ€xvupwxjkgffc\[`hbfjfofddb]ZUUVW^fdiihhik_[\]b\X\W_]W`[!!% #0.Nc~š ¥®­®¬ª¡™ŠqWD''+GG_uˆ˜Ÿ¥­¬­³°¯³³°³±³´³´²²¯¨¦¤’‹›{kU45/)-3493+-..-.-2./3--8209A=501+:AGLGA9:4-57<10+0<7DG7+0'-(#$1?50-118CPBG9:23<<3,/7.1CFKD,1+%('&6H5+'-?WjGR9$">l]AKP[agzxzxsav‚viqƒ…{C)0G}iA?8./.3-129CGKSX\\kqy{z€€~€‚€‚€yaR50PWeffv†ƒ…„„ƒˆ…‰‹…ŠŠ’ŽŠ‡ŠŠŽŽŠ‹Š…„„‰…‰ˆ†ŒŠ‰ŒŠŒˆŒŽŽ’Ž“•”“–”•›™ ›ŸŸ¡¤¢£§ª¦¤ª°¬«ª®±¶¶¹¼»Á¿¾¿ÇÆÅÇÇÉÈËËÐÒÐÐÏÐÐÑÓÕ×ÒÖÔÖÖÖÔØÛÚÖΜ)!]]4&-VlVGNarakuty{dji`ruuz“k1+1jv)*0:)''/;D>=?;.4L=.zˆ“‘—‘–𔕓’’‹”•‘‹‹‘ŽŽŒ‹‰Š‰‡†‰…‡‚„€|€‚„†‡‚€„‚Š–ªµ¿ÊÌÍÊÌËÉÇÉÏÒÖÛÜÛÖÕÓÌÆ¿±¡zuvuxqpmlhphgaZb_^cbij_Z]TLRXWfcejihccci_]]`e_]`[[]Y\]``&&%)"!&'2C`uˆœ™ž©««©¥Ÿ›‰z^D/-4AM`w‚’—§ª¬­°±¯±°²±²±³´±·°±«©¢šŠ’¡£qi6;7;3'44++,.)()492.+3.:EED=B1(0604@G=<8>?C@-+)161133KZ6)'*+-3>B.#)>^[>6UlT<=>UhfSOeNAWfŠƒgnj]KZr}‰•ŒŒW.6Xs+,6/*'0.;?68+0;>6B542(,--39MOSVUdijqx|~z}ƒ€…€|xmSH48V[gnl}…„†‚…ˆŠ†…ŒŠ‹ŠŠŽŽ“Ž‹ŽŒ‡‰…‡Šˆ…‡‰ˆ‰ŽŠŠ‰ŒŽ“––”––™›šœ›™›¡£¢££¤§§¤ª¨§¥«¬±±¶··¸¹¼ÀÂÁÃÂÇÇÆÊÉÊÊËÎÍÍÍÑÐÐÓÑÑ×ÔÖØÖÕ×××××ÕÀg#E{}‰Ž‘”‡œ“—”’’•’ŽŽ’ŒŽŽŒ‰ŒŽŽ‹Š‹‰ƒˆ†€„‡€„‡…ƒŠ„ˆ‡Žž¬ºÀÆÈÆÈÅÈÆÉÐÓØÚÛÚÙÕÑÍɺ¬™‰}wrtrqospfmkbaa\Yceeje]VMIKNU^cgijhlfcdc^ZUbc[^a_d]X]ebc!!#$"",2A^sˆ•˜¢¨««ª¢¢˜ŠzbJ6/4P]depyƒƒ…ƒ‚†‰‹Œ‹‹Ž‹Œ‹‹Š‹Ž‹ŽŠŠ‹‹†ˆŠ‹‡ƒˆˆˆŒ‹ˆˆŠŽ‡ŒŒ‹Ž•—’‘•”‘•••›œœžž¡§¡¥¨¦§©§¦¨¬­²¶¶µ¹¹½¿ÁÃÃÁÄÇÅÊÇÈÉËÌÍÍÎÐÎÐÕÐÓÔÖÖÖÔÖÖ×××ÖÕΓ0Nyz’‹”Œ“–”–‘Ž‹ŽŒŠ‘ŒŽŽŒ‹Œ‹‹‰Šˆ„„‚ƒ„††‡††‡†‰’¨¹¾½½ÀÀÀÄÉÍÓ×ÛÚÙÖÒÏÍǽ§˜…}tsqoprmkeic\`^YZ`egf`YPFBLP\dhhhigdfae\[W\]^^X^`\d]_d_[%%" &&"&4I]yƒ˜ž¡¨®°±²°±®¯²³³¯°²µ«¬¨¢ž™’ žˆy71GF/'=3**.%%')95247UceV;86/,--,4>D<<=:?6101@/0<36(31DW)#,.-4=>5&,VcF@6DMfbA'%<}~ig‚\s‡}ˆ…nXPE7@Uswˆ”mRY`.9G<$&%3B=<76/);>6I:5614112A2/'6,/4<@JPWX\mimrtx~}~€€€v_I63HTbgls}…€…‰‡‡ŽŒ‰‹Œ‰‰Ž‹ŒŒ‹ŠŒ‹Š…‰ˆ†‡Œˆ‹Œ‰ˆˆ‰ŠŠ‹Ž”Ž“•–‘’“”˜–Ÿ™™›œ› Ÿ¡¢ª§¥¥¤£©«ª­¬°¶³¹º»»¼ÁÃÁÁÈÈÉÈÇÊËÍÍÐÑÑÎÒÒÑÒÓÕÕÓÔÔÖÔÔרÕÑ®IS{ƒ‹ƒ•’‘“’‹‘‘‘‘”‘Œ‘”–’ŽŒ‹ŒŠ‰ˆ†ˆ‹ƒ†‡…‰†‚…ˆŠŠˆˆ‰Œš¥­´µ¶³»ÁÉÐÕÙÜÚØÖÒÎËŸ¢“ƒyorljpoiiedbWb]\aefha^ZSNFNS[cfnngebgg_X[W[^\_[dc\b`ba`T++(&(#%#/9Ul‚‘™£ª¬¨®©¢˜ˆ}fT545BN[r†Œ–ž§©®°±³¯±²³­°²±±µ·ª«¨¥ ‹œ£˜„i93WO61<)*&.&,,/12:HQcZH9312-*-+69CE?=L:/50-2==5:87/-8Dh5**276/:A5:XI=D:EJirN'&%G…“”œ€mwYŠ~slbZMY`dox‚€„‰„‹ŒŒŽŠŽ‹Ž‹“Ž‹‰‹ŠŠŒŠˆŒ‹Š‰‹‡†ŠŠŒ„„‰Š‡‹Š‹ŒŽŽ’’“•–™–™›˜œ›šœœŸžŸ¦¡£¤¡§©¬©««®´±¸º»¸ºÁÁ¿ÁÃÉÆÉÆÊÌËÌÐÐÎÍÏÓÒÓÔÕÖÕÔÔÕÖÖÖÖÕÔÂk\y„‘Œ†|…Š‹ŽŽŒ‘‘‘–“•“‘ŽŽ‘ŒŒ‘ŽŽŽˆ‰‰†‡ƒ~…‚‡†‡†ˆ‰‹Š‰ŒŽŠ˜ ¥©¬©²ÁÉÑÕÖߨØÕÓÎÇÀ´¢zooilkhhmacaX[]_eibb_]ZPNFRXbfnpjfbdecZZVXaZ^`^eY\adg_YU,,,/(%#(+6Qh…œ¡§¬«ª££—„mXA65CO]u„‘œ¨©­±¯°­³²¯±°¯²²²´²«ª¦Ÿž™¥Ÿ…_A9eU4,7$%%)-#(;6@U^]O9.2'*.+.(+6>BA=<=22.914;E>:<8,3.8^C'4:<5*4MMRI699<;:Poh8&%M‹¦¡vm¡‚j‚r[dUG;BDiqx{w€M†¤Š&",.5;:5205?1>?63.9500337,.,-24@KUVUVemsvtyz€}~€z{vmW:0ASaagmzƒ…‚‹ˆ‰ŒŠ‹ŽŠ‰‡‹ŒŽ“”‰ŒŠ‹Š…‰‡‰‰ŠŠ‡ˆ‰ˆŠ‰‡†ˆ‡‡‹ŒŽ‹’—’“›•••–™š”žœžžŸœ¡Ÿ¢¤¦¥©¦ª®¬®­°°µ¹º¸¼¾Ã¿ÀÅÄÅÇÉÇÉÎÎÎÐÏÑÑÔÒÒÕÓÔÖÔÔÕÖ×ÖÖÖÒɉfxˆ’‘ŒŠ€t{ƒ„†††‡ˆ‘ŽŒŽ’‘“””‘”ŽŽ‡ˆŠ‡‰…ƒˆ†„ŒŒ‹ŽŒ‡ŠŒ‹ŠŽŠ’™ž¢±ÃÌÑÕ×ÝÚÕÔÑÍʰŸŒ€wqqqpkfeg_^ZT^`bhd_^^UMKKTS]cfonjhga`bY]XV[X[e]d`_^a_[QK**& $$'1Nh|šž¤¨«¬¦¥š†s^B::DI\q†Œ—¥¨¬®°³¯°¯¯°±­²³°³±«¨¢Ÿ™Ž•£¤†b?=`W852&('**+?NNZ\O>@1/.0*,))++2BI58H<11/66;E@CFH;3)+,G@17=2.)-S`a=1/5668Ef{Y6#%+HcpUoˆuo|ƒWTK;1?3^zsœŸ||s¨ªY&%6-1:3*/(2=73010137/2,)140'/2:?JPY]bimvvzyy}~{}|~{rbP6,BT_crv{‚†…ˆŠˆˆŠŒ’ˆŽŽ‘”’‹ŒˆŒŒ‹ŠŠ‰ŠŠ„‰Š†‡ƒ†Š‰‡„†ŠŽ‹ŒŽ’”‘™‘’˜˜—š—œ˜›ž¡Ÿ¢¤¤¢£¥©§­¬­¯¯µ¹··¹½¾ÀÀÁÃÅÆÊÈÉÉÊÌÎÏÏÏÑÒÓÔÖÔÖÔÔÓÕÔÖÖ×ÖÖÍ«mw‰‘Žˆxqvx}€‚‚„Œ…‰ŒŒ””Ž’•ޔޓ‘“‘ŠŒ†ˆ…‰‡ŠŒŒ‹Ž“‹Ž‹Œ‡‚‡”ž¯ÂÌÓÙÛÝÚØÖÑÌÆ¹ª›‡|uonmknea^aYZZd]dabdZVRNIOSUagllojea\^ZX[YXZ\^bba_[bh[UQH'')&(( '15Idt𛦩¬¦¢¤šƒx]G9;?K]q‚Œ›š©§«®²±±­¯¯±°¯´²³±²­¨¥ š’‡Ÿ«‹hAH]\.<21**1*0DPPOD6032/102300,29EB8CNA:73<>:D=DE>:;0.0@I:H9(**4Lnh8)+-380C^h_=C**1?:9Qamt~’jRE<,93IWsƒ~­›“—°”9$-21;92-),;>7044.32.:+/.2/.1/8=CHP^ajmrtwwy{‚~{}z{|oVD57N__br{€„‚†…ˆ‰Š‰Š‹ŽŽ’Ž‹’“‹‹‹‹Œ‹Š‰†ˆˆˆŠ†‡Š‹ˆ‡Š“‘‘“““–‘“—˜˜˜››—›š››› ¡ž¡£§¤­±¯¯¯¶µ·¸¶¼¿¿ÁÃÃÅÇÅÅÉÉÊËÏÐÐÒÑÔÓÔÕÓÓÔØÕÒÔ×ÖÔ××Ô¿~z‘’’ƒikmszqxx{|ƒ„~†ˆ‘‘“•ޒދޑދ‹ŒƒŠ‹ˆŒ‘Ž•’’”Љ‰ƒƒ’£³ÅÎÒÛÝÜÚÙÕÐËÁ·¥‘†vsrrnkg^bfa]YXZ`f``_VOTKJRXXgifglfd__^[YWT^V]\`^_]acfZTKE((%"!!-)*3F[r˜œ¢§¢¢¥Ÿ™‚qdD2;?NXp‚”œ£¦­­±°®¯­¯°²¯µ±²´°«­¤¢›“†‹•®‘k:N]a277**1+128=7<44393.3,050,11:C77OHC7510UoW5,+'+74GjkbACO*"1/-78Db•ƒnR9,01?EH„{œ³«´£j),324;=.1+4@69A450370-.+4-0*1,8@KPPUdpmnpvt{z|z{|{yiR7/CTbXkt~‚ƒˆŠ„‰†‰‹‹ŒŒŽŽŒ‹•—‘‘‰‰‰ŽŒŒ‹ˆ‹‡ˆ†‹‰‹ŒŠ‡‹ŽŒ““”–•–”•–—š™›™žžšžŸž¡Ÿ ¤¦¤­­®­®²²¶µº´¼½ÁÁÀÅÄÃÇÉÉÊÌÌÏÑÒÒÓÑÒÔÓÔÔÖÕÕÕÖÖÔÖØØÊ—z•‹{vabglrhoputw~~‚††‰ŒŽ’‘•“Œ‘‰Š‡Ž†ˆŽ”‘•—‘’‘ދЅ……¢¸ÇÏÖÜÝÝÚ×ÓÐɾ°£Ž‚xrmplljje\`S^[]fdf`WTXQLOSZ\dhijbe_`\_XRX]WX]`a_\Y_c[[TG?%%)#'#&+$-Of’˜Ÿª¢¤¢ —Œ†w[F:;=M]p‰“𢦍®®¯¯°±®®±¯°±³±°±§ª¤ž”‡‚Œ¨’b8G`E<:1))1*2+8223.050001),<.*.4;=2=J=A8;2B=O:=.8<2498?:?.',#$*;[gA7/()-6LWuyoZ4RM%.A42))7Ps˜[<,7;SO4Ypw«ª©z5!'-00;/-('2F7472,1220*((+05/43:@NJOZhoqtvuuz~ƒ‚}{{yo\B12DV_`m}‚‚ˆ…„†‡‹Ž‹‰‡‹ŽŒŒ‹“‘ŽŽŒŒŽŒ‹‰Œˆ„‰†Ž‹Ž‡ŒŽŒŠ‹’‘“–•“–——•–•——š™ž  ¤¤§§¨¨­«¯±²´µ¶¹µ½¿ÀÀÁÈÅÅÅÊÊÊÌÎÌÐÒÑÓÒÒÓÕÑÔ××ÖÖÕÕÔÖØÙÑ­Їvo\VY_p^gjnkquwu{‚ƒ…ŒˆŽ‹‘”‘•‘ŽŒŽ‘‹Š‹‹‡†‘’“”š••Ž‰‚…‹˜•¤¼ÊÏ×ßÜÝØÕÔΟ¨š‰|utpqkibe^[[T]^fib`YPJMJOT[]abikgdieg__[W_^Zbaea_\\_XYPID;''&*' "(%0Mi—›¡£¤¢ž¡–†waIB==LVn|Œ“˜ £¨­¯±®±±°°±°®²²±®¯©¥¥›“‹}ˆ’];K]@3@4-*))(-./2,1370-,.,7>4+2=K59CA97547?LPC9.-4915AE7.+)'&)+GhP1461++6Hk{xwoC6_=+=;3))$>]›–^3/5BWYD\AW€Ÿ€[&!13196/'%*9?8334*+7/,,%.(/;/+1,@G<53=:9D`UK;3*(1+/13/13.+&'+RZ7,765/.5Fd|yt†i8CU<050#//6Ex‘u;3/>flogJLUš{K)'105D4%%'!#!$#*4Ees†’›£ª¯¯ª©¢—‰q\E==AP]lˆ“›¢£°­¬«±°°±®±±²±µ´±°ª¢¤ž”‹€|†‡aHMaI=D,&(-.,32354-80/0+2,0032=H91EJ=40.59GPRH;.,*1*)/872,,)+&4PY0'1AA6/7=Oy}{}ŠI2EL@,' (/7=CFFOU_iposwssuy~{{yqVD62=Q[an„Ї„„ˆ…††„‰Œ‡Œ‰‘‘‹”’‹‹Ž‰‹‰Ž‹†Ž‰Œ‡ˆ‡Š‰‡‹ŠŠŒŒ•‘‘•‘•œ“•—››™›™›˜™œŸ¢¤¡ ¤£¥¦§¨ª¬©¬ª´µ¶µ¼½»¾¿¾ÄÄÄÄÈËÊÌÎÐÑÒÓÒÒÓ××ÖÕÙÕÖØÖ×ÖÙÜÙÓ®€uXRPNGONbLLRWUVa_bcfgjmpps{wyz}†„„…‰‡ˆŠˆ‹Œ‘’—‘“›˜šš‘‘‹Š‰„†…†‹—¥¸ÆÏÕÙÛÝÛÕÐÌż­—Š~upkgf[VMOMNVY_febZSNGEKSO[anfead_[bc^[]Y_[ahficZ^^UWRL@=A5##$!()#(.E\o„”§®¯®°¨¢ž‹ybC>BBJWl‹”𡥫«®«­°®¯±²±³³²³³ª«¦£ —Ž|‚waSN_QJ>,$(*)/3:;A6/160:+.0,;6JO>5IMECPQPQZY]YU`aebhnosvuqu|‚~„‡‰Š”—›œšŸ˜‘Ž…}ƒˆ’›¯ÁËÓÕÜÞÝÚÔÐÉÀ²¦ƒyumeeYZWMKXQ\]`c_^WKIGJMURckkid^`_eb^]_\V\^hjiia_^]S\OND<6;$$&%##"%)./-34SF*Huzoj\d‡„ˆœuC:16IcL+1F47?<,C3220.//,8//,5AL783-CE>4+27=WW_ny†…†„‹Š„†‚‡Šˆ“‰ŽŒŽ“ŽŽ‹Œ‹‘މˆŠ‰ˆ‡Œˆ‹Œˆ‹…Š…ˆŠˆŠ‹“’“”—’•—˜š˜›œœžŸŸ £¥¥©¬¨¨¯¬®­­°µ»»»¹¾¿¿ÂÃÆÇÈÊÊÍÎÏÐÏÑÐÔÓÕÔÔÕÖÕÖÖØØÙÛÛÚÕ¥^IQTXGX_TIRPQNLOHQJILRQVSSRROXUX]\b_fcdeon{z†‰Œ‘”žž œ—‘Ž‘‘•–‹”¦½ÊÒÙÚÜÝØÔÎŧšŠwk_\UURIKGLSYbc`^ZSHHEQYagchmf]]_\a_]\[V__eciee`\d]WKKF;726@G669946-./,1+89911%10Gt/"$0_t[P]j„pw¬žpUP80/ALCZV`[R^b—u,)53;777573.+*%(1A/((,/-.+-+.+1);::?HTOS[dspwsy{{swvxvxts]9--0EYcfu~‚†ˆˆƒ…Љˆ„†Š‰‡‰Š‹‹ŽŒ‘‹‰‰ŠŽ‘ˆŒŠŠ’Ї‡Š‹‹…‡†‰ˆˆ‰ˆ†‹‘ŽŽŽ’‘’’“‘•”’™•š•›™šŸ¢  ¡¥¦§¨§ªªª¬«­²¶¸º¶½¾¿½ÅÄÅÉËÉÉÌÍÏÎÏÑÑÒÒÓÓÕÖÖÖÖØÙØÛÙÙÛ׸bFRTZTY_SSQSRNQSJRSMJIKFMHHNLNPQQSVN^^Zadlmp}†•™››šŽ–¢ ›š•–“š­ÀËÓÙÝÝÜÖÓÊ¿³œƒxka_UOKHEJPTV^`ZWWQOKKPT^dhkgcd`_Z_a]Y[Z\g\fioi_gbaZWOEJB:-54''%%""'/FoŠ—Ÿª«°°±¯ª “‹~lC<@FWgx…‘•£§©«®­­±¬¯µ¬°´µ´µ°®¦¡ ›‘‡r`p`D[[`8'##,37.''0.,(--./.@EG@3,./=GA01124?D@DG?7=6B<+*)&//12544092BU&"'DfaITd€dެ™jqX+17WgZm‚sb?=Z=-/0-/0760>13-)&/051)-++6,.),5)+./:9>JONW_kqwzxx{vttzwwuqcC/*&2E^ikx‚……‡†‡††„†‹ˆ…Œ‰Œ“‰‹ŒŠ‘”ŒŠ‰Œ‹ŽŽŽŠŠ’Œ‰Š‹‡‡‡…ƒ„‰‡…ˆŠŒŽ‰Œ’’Š‘‘“’••‘™˜˜˜–™žž¢ž¡¢¤¤¥ª©¨ª¨¬¨­±´µº¶»¼¾¾ÂÂÆÇÊÊËÊÍÎÏÐÏÑÒÔÕÔÓÓÕ×Õ××ÙÛÙÛÛÙÆyMJTVQa`VZ^YTWRSMWLNDH@KGJA?DFJHLGKHQPKJSWaeuw~ˆŒŒ–“›¡©¥¡š—–𣶯ÏÔÛÞÜÚÔÐź¦™Œ~pbZPLG;>=FIPUX\ZSVOJLLQ\bcln^V][^`]_d^V[Y[aegidX^b^\QPO?7:335''&"!!"!)4Gg–Ÿ¨¬°°²¯¨£š|lE5=KSku†–¤¤ª­¬­±±°²±¯°²´´´¯°©£œ›“^Rq‚eSY]_9&$)2;4)-1/..)-*-0HHH?60/44LK9/.2339FACE?D=?@D?AC?BBRVX\kwy{€‡“ª¯°ª§š•“Ÿ¬¼ÈÒÕÛÚÚÙÓÊÀ°ž“…si^SNJA:>AEC=3:4()/7=*847520$$+#,4.);agZf{~q}’hLq€WjWf}s]H1'$/+-.174;4,.(--21*))&&-*,'),))/37;;FXXW`hpuwrruxvvwyxulO5'$/4MWfny‚‚ˆˆ‰Š‰†Š†ˆˆ‹ŠŒ‰‹ŒŽŽŒŽ‰Ž’Œ“ЉŒ‹Œ‹‹‹‰‹ˆˆ‰‰†‰Š…ŒŠŽ‹‹‹““‘‘“’–“–—“˜—™›™›¢šŸ¢££¥¥¨¨¦«ª±°²´·¸½»¼¼ÀÁÂÇÉÈÄÉËÑÐÐÍÑÑÐÐÔÒÔÖÔÕÕÖØÙÚ×ÚÛÔ¬XENZYd]`b^aW]^`WWRXLULNDKD?C?;>4:86240548;AWPQYYLJFJQMU\Xlmoif`[]_Z\X[`X[\Y]efejfac][YOE??99745++*)*&,-)'1Gd†—¢¨¬³´¶®«¤ž‘…oH=9K\cz…š¡£¥ª©¯²²°®±¯­¯´´³µ³¯­§ŸdB_vv^STQV7'-.88('&'-,%*+0;NT52;6/+/1IF61)16;?97<6**2>+-:>>:9,)/5;8-$,Glj^o‰{s‡”Œis‹_f^ONcp]I/(.'+/036=3,.,)+61.*'&)')'')+',,260.0A@6-++0DPF?3:?698?84=:4223-')/8?=4*,28//$(,Lcmmq‡™“xdv’ˆknrPX_9CnpVD*'('0/7;1.*70-3..)+*./+(")*-+*,38BDPU[^ilqrrwnwwsxxsqhG2+%+7O\fr|ƒ„‚„†ˆˆ…‡‡‰ˆŠŒŒ‰ŠŽ‹ŒŒ‰ˆŒŽ‰Ž‹‘Ž‹ŽŽŽŽˆŒŽ‡‹ŒŠˆ‰‡ˆ…ˆŒ‹ŠŒ‰‡‹‹”‘“š˜—™™ššœšš›žž¡œ¢ ¤¤¢¤©«¬­±°¯³¸¶º¹¼¼½ÄÃÆÅÈÇÌËÍÍÎÎÐÐÐÓÑÔÔÖÔÕÖØØØÚÙØØË‰NQT]``efdffj_Zb\Z[XZTQNMFD??=;:49/-/.1--,15;A\Woˆ¥·ÂÄÂÁ¼³¨ ¡¦¹ÄÍÒÖØØÖÓÌÀ±¡‹ykWMKD;9:?>IPMSRMOKINZ_chdpkqie_[[XUYVSY\][aebjibcb`XULI;:<93302000186<<697Ll€”£ª­³µ¶µ°«§žˆlO==G[dw…ŽšŸ¤¥ª­®°¯±±°®¯±³º·´°¯­§¤™‰`Y[|†_D><;2'&55*/,//9,+2A5957<>=>;80*;20,(-'2=>9=/271+(&,,C\jho˜¦“yfrŠ…eYaMbZKc^NK-)'*,572+(,-03>;;:,.+2(&$***((.84335..9FCGECABDTgy¢¨°²¶··²±­›ƒhU>AJZhx…’—žž¦¬ª®±®²­®ª°³²¶¸³´°¬§£™€\^X‘hGA<1=*,27%,(+(31:?7.,132/7?-(,-17>8+)6GDG8??8:97=@818B5/&(,##'0AH:'*6:4//.+%<\ghƒ¥€q…uIYb^yt|pU@(%&64770)'%1(+*-,*'$%.'%'((/20:GLFLV[bjmptstvvutyykV8,)+/>L^ktƒ€ƒƒ‡ˆ‹‡‰ˆˆ‰‰ˆ†‰‰ˆ‰‰‹ŠŠ†ŒŽŽ”Ž“‘ŽŽŽ‹‹ŒŒ‰‰‡ˆ‰‰††‰‹‹Š…‹ŠŠ‹Ž‘”“”‘”˜›š–ššž›šž›ž ¢¤¤¤©¦¬±²®±³··¸¸»½ÀÁÂÅÃÅÆÈÌËÍÎÏÐÒÐÏÒÓÕ×ÕÕÖØØØØÙØØÔ¸fQV\_dcjkfhdfhffg_`[YXUQRQHKE==7253..&'"#%'.6Ae‹©¿ÅÊÊÊý«Ÿ˜ž°ÂËÑÖÖÙÖÓÌÁ·¢x`OF?;9?9BFMLTSTRG@DJSZZgepllfb\YVVSSP[OP_Z`aklhggd\\WRE;;B76:439;;MQNLLJDHFUg{›«²±··¸´²«š†sZE6D?44<9<@1430,*'(4+&).9.10*3$%%1&#*Ds”µÃÉÍÍÓÆ¹¢™•£·ÅÌÓÖØØÓÑȾ«”{]VKI5;47?E<65AK97N7C>221//*/)43,'&+4<8.6\E5<311$$3Mhƒw’Ž‚†xh\_ze?]‚xl,)+,38-.4)2*.)-(*)/&%$+*1))-.>76<77@DHHMNMIIFIJR\_gikthc]VTPJSTUQRRTOT^fkmtuff`YZVOJPMEA>>86<__]^XUSZ[UZgsy‡™¤©²¶·¸´³¦ €kODRWiv†Œ–ž¤§««°¯°°°°°°²µºº¸±¯¬¨¢Šp|`mz_TV/&&*22(:B>9<908--4..,502-4*4/8+795BGB25=DM9;D;@=0/,1//1*-566,%#6LH4IOHLPUahrsvqwswu{zrg?-*)@.:PWevzƒ‚ƒ…€†Ž…‰†…†…Šˆ‡ˆŠ‰ŒŠ‹‹†Œ’‘‹ŒŽ‘‹ŒŠ‰‰Ž‰Š‹‰‡Œ‰‹‰‹Ž‰…ŠŠ‹’’”“‘‘—™ššš››™Ÿ¢¢Ÿ¡§¬¬©¨«­®°µµ¸¼¾¾¿ÁÂÃÆÆÈÆÉÌÍÎÎÑÏÐÐÒÐÐÓÔÔÔ×ØÔØÙÙÚØÄvOTUXbhjmlfkkllehhf^a^TZ]VUNJQID=<;5/0'-%(,+Cl™³ÃÇÌÌÊ¿­ž«¶ÀÆÎÐÐÐÉż¥ŽtYB6@65;A?JDKJLCDDGBKTXcfjknga]\UURRLMQTNVXY\cjuqroigc_XRLIOHD>=:9;BYYZ`]\Z[eb\hwzˆ™¥ª²µ·¸´®«¤•pTKWbesƒ–œ ¥¨¬°±´³°±®²°µ¹º·´¯©¦¡‡rpap}|c][9,,''6,2>2/340,,3301200-3313113022DA8+/>IP;?I<>AB+,/0)*,)1897,(';OE5G=7<4/,55D@CF]t€˜“†eGXg€xK1,46-1&/+*,+++*/)$&(##)1//17?MMMKR]hgustp~vuwzwbK+($*-NNZctz~€„‡…‡„Ї‰‰ŠŒ‹‹‹‹ˆ‹‰‹‹ŽˆŽŽŒŽŒ‘ŽŒ‘Ž‘Œ‹Ž‹‘ˆ‹‹‹‰‡ˆ†‰ŠŠ‰ŒŽ‰‹‡‹‹–“––————œ››™  Ÿ¢¥¦©¨©¨¨©®®®±¶º¼¾¾ÃÁÅÈÆÄÉËËÌÍÐÑÎÍÏÒÐÔÓÔÕÕÕÕ×××ÙØÖÈ‹MQUZbailljlnllihbb^i]WZ\[UXQJRKBB@;0.(-*)+-Jp•®¿ÅÉÌÈÁ³¬®®»ÃÇÊËÌÊÀ¸­•vUG95655:=C?EPSC@ECELPSabgmmnkbYQPTMPLOONOXX`agiquveb_`YUPSOTDB>9<;ECTTYYZUZ[cadl{„¥«°···¶´®¦–ƒsZERbfq€–›£©«¯±²±²³²±°±¶·¹¶´¯«§ †fmbr|v[VR>-+*242/,+.1156,-.0,+874C?1423*-*.:11/9?BN<4B71<<;:,+/-2*,6A@72#0=J;IA=<6:BAA9>GO>FJd€uŽ—€^bwXLA3/35/++*+(.-(-01(##$#%*-6..4:EIMPRSkjjtz}svqytuf>+%$#,=LVeo{}~‚…„„…ˆ‡†Š†…‹‹Š‹ŒˆŒŠŠ‰Š“‡Ž“’‹‹‹‰Š‹‰‰‰†‹ŒŠ‰ŒŽŒŒˆŒŠŠŽŽ‘“”‘’•˜š–™™–˜œžŸ¡£¢Ÿ¤¤¤£§§ª¬°¯³¹·¶½»¿ÁÂÁÅÅÇËÉËËÏÐÏÑÏÐÑÑÕÔÖ×ÕÔÓÕØÖØØÐ¢bKQY__fkfnkkmmpkkhbbYd[_YUNPRPJH?F<54.,5199Gl§¹ÂÈÊÉÅ»¼¹¹ºÅËÈÈÆÄµ¤—xZA;875167;EFGONEBAFMITXakjkib]XRNHMJJOJKOTW^ccekrptjb_^TSTVVQL?=;9;DBVV[^[Y[bbfmr{„¤¬®³¶¸·´¯¥•ˆw]GQ`gt€‹–£¦«¬±°°±µ®²±²´¹¸·³±®ª¡ˆdnft{vZXQ47,18-)-2..410-.1(3+.456A<4203/10*/+/3:HFR;1:72>AGB-*011%&69?F:%-0:@IJ,621?CV:;AJU;;HsLh†Œ{x{>40*012/33).65)+&2)((#!''125.6/##",4OWfku~€ƒ‚„…ˆ‡†Œ‡‡‰ŠŒ‹†Ž‰ŽŒŒ‰‹Š‹‹‹ˆ‰‘Œ”ŒŠ‹Ž“Œ‰‹ŒŠŠ‡Œˆˆ†‹ŒŠŒŽ‘Œ‹‹ŠŽ‘‘“•˜˜•”–™™šž  ¡ ¥£¦ £§§­°±®³»·¹¸¹½ÁÀ¿ÃÅÈËÈÌÌÎÎÏÒÏÑÑÑÐÕÔÕÖÒÐÓÖÙÚØÔ¶kMSW_`hjninmklhehbjb^c^\^XRUXUMFGB>;744-078Jc…¡µÄÉËÉÇÆÂÀ¿¾ÂÆ¿Á¼®¥zYE>6334456AGLCLA?DDNNTZ]ckkl_cgSRLKLPRRMLRPVZcjlqrshghe[VTQRSIF:;>@>C@VV^ZWbcbdlnuƒŠŒ §ª¯´µ·¶³°ª™‰w^HY\ftƒ‹“š§ª©­±¯¯²´³±µ²¸¸¸µ³±­¨¦‹\Yo{xq[XJ,,--90-+0.1+29/.2.0-2627A;.,:3760/*,*2<;?D969/.FHRS61271+%/C;UK8*..8GX33;/5Gh[70ETZFMwGHdx‰Ž‚k4+,.15-,)1+/7+*+/,,)%"")+,356BOLHFV^chgnwyvsuurY:,!%,2=L\chtx|}|ƒ€……„€ƒ‰ˆ‡Œ‰ˆŒ†‹ˆŠŒŒƒŒ‹‰‹Š‹‰Œ“‘‹’•ŽŠŽŽŽŒŽ‹Š‰‹…‹‹ŽˆŒŽŽŒŒ‹Œ‹’‘Ž‘’‘’–“”˜–˜››š¡¤ ¦¦§¢£¤§©¯´®±´³¶·¹À¿¾ÄÁÄÆÈÉËÌÎÐÏÎÎÒÔÒÓÕÔÓÖÓÑÕÖ×ÙÚÔȉOQVYY\egjnqqlmhhgfaab_\_WUWWPMLKKDBB?4;6:>IZ€ ·ÂÈÊÉÈÉÈÁþ»¿´±© vjPE@24695:@EELE?AAGGWUb`bfhgfa]^WTKFPKKNRTRUZ`hhlrulia^\_VRRLPDE=;^L,:/.@]nK08Ai^hPBUfy–›s@233/.--20/21.*'+,*'+*+2--/8:IKEMQ^geenqyywpqgN*)!',2ALRdery}~{|~}„ƒ€ƒ‰„ˆ…‚‰ŽŠ‹‹ŒŒ‹‰‰‹Œ…‹‹ŠŠŒ‰‹‹‘’“Œ‹ŒŒ‘Ž“Œ‰‹Š‹‰Š‹ŠŠ‹ŽŠ‹”“‘““”•–˜™˜™žŸ¡£Ÿ¤§Ÿ¢¥¥©¬°­®±±³¶¹»¿¿ÀÀÃÅÆÈÈÊÌÍÍÎÐÒÑÔÕÓÒÖÕÔÒÔØ×Ø××ΟaQTT[a^gfimloihigk]]ae^b^YaUTRKHTJHGCBDLJKO`€Ÿ¶ÄÉÎÍÎÏÊÆÃ¸µ¯§¡˜‹t`UL;624496@@BJL>AAAALTX_anihig\XZOPOJSLMLRNQYeblqrtohea^^bVYQIDD?4?>DJGEIIV]cckklsx{„‡Ž›¨¯¯³µ´±«¬£™‰{fKR`gtŠ’œ §¤®®¯°¯³²³¶¶·¹¸³³±­§¡•ZM{~wh\L4)('82105/,-03(4-;:4++906A70-.44-*.03ACIA<;DDQWcgqpsrsy‚„Šš¦¬®°²°¯°­£–ˆybQS[iq„•œ¥¥©²¬°±­³¯±´µ¹·¸·±¬­§¢“cNx€sl\P4)5):5)/354-18+07@85-4159C81*-0,5..5F>QQBOL=,-9=LVXU=*),76+%/THYgQ=799=mC*8K;BXWOCPWy‹–v@:O05Fbc?*1.,-/)*114-++%*##$%,3/2;EFNLQYhbfmijkreO6'".#+.;O^cfipuuw|x„€„ƒ†‚„††Š‰‹‡ˆˆŒˆˆˆ‡Œ‹Š‹‹ŒŽŒ‹ŒŒŠŒŽŽ’Œ‘ŒŽŒ‹‡Š‘Œ‰ˆŽ‡ˆŠ‹‰‰Œ‹ŽŽŽ“‘––““’•žš›š˜Ÿ¡¢ ¤ ¤¨¥ªª¥§ª¬°¯´²·¸¿½À½¾ÄÇÆÅÊÉÍÎÏÑÏÑÒÑÒÓÕÕÔÒÓ×ÙרÙÕÃ|NNWZaf`cdihkgffldh_\]ch``bc_SURSNNLUOVPOW\sˆŸµÅÍÓÕÓÔÍÆ»«¤’†}b]UM=@7545A?CLQJHFDADJRVZagjkmei`[TJIKHJIORMV[[chkouqndhb^XbZYQOJD;>DGFB>7:/-@6-.9A61*62168=721/',-0*7EZT=`^<20@:FRV\S;*-+-.+*C[OGTG@:G2]b2.DE:4QPMFQ\„œŒkMX:$3MiG,40(4.+-64-)*'%*&#$$*'+07FKMQU[eenfdgTN:+# ',;JU_ffjosuvzz‚„ƒ…ƒƒ……‹‡Š…‹‰ŒŠ‰ŠˆŠŒ’‹‹ŒŠ‹ŽŽŽŽ”””‘“‘ŽŽŽ‘Ž‹‘Їދ‰ŒŽŽ‹Œˆ‹‰ˆŠŒ“”™“‘”•–™—œš™¢£ŸŸ¢¡¡¦§«ª¦©§ª¯¯³³µ¹º¼¾¾ÁÃÅÅÇÊÉÉÍÌÌÌÑÓÒÓÔÔÕÔÕÔ×ÚØÖØÖÊŽQUPZ[__fhbmjefgkhdbecee^c`bXXSWUQWTZXZURR^uŽ¢µÃÎÓÕÓÑÊÁ° ‚p]UMJC?<7<;7JECJMLJ@NFJFCALGPZ]dihlifa`ZcTJHFHEEDSQT[]]inqrsnjeifg_\^PHHB6@D@FIEK@:77MU^hpoqsliow„Š ¤­®®²¯®ª£“†wcUX`gn“œ¡¦ª­­²°²°²±³³¶··´´°­¨¥špDVvmka>,'%)0).-0>.-?:/3;C9.+3.1+ELGD30(-3/-6ORB<€a@0,72?DC9ZOUCGaŒ–‚HOG(9IBS9()&"(-11-)&+$%-/&-)*,6GHPPWR]ZRC:9I*&.'$2;CT]`ddimpvwwvy}||~{„ƒ‚ƒƒƒ‚ˆ‰ˆ†ˆŠ†‡†‹…†ˆˆ‘‹‹‰‹Š‘ŽŽŽ”“Ž•’”–’‘“’‘“ŽˆŒ‹‹‹ŽˆŒŒ‹Ž‹ˆŒŠ’‘–”–’—••™˜š›žžŸ Ÿ£§ªª¨­­©°°®²µ¹´»º½¿ÄÁÄÆÇÉÈÈÏÍÎÏÓÑÓÖÖÖÖÔÓÖØ×××ÕÒ³hIOTY`a^ahcdfbhdggfb_idacgedUXWW[Xc\^`_[W[q‰ž±ÃÉÎÍÉÁ¹ª—za[NNKL@AE;KEEFGE:600BRV]fidffegu™¢¥­±³³²¬¦™‡yfZWajq}Ššž¤ª®°°±²³³µ´µµ··³°¯ª¥£›vPUogm[3,+)+/902>51366'4~`;0/:AWXj`Q_L-(.74,*FnWR4FB1,*%16/2*%),-(/43,+25:BDIPRF:721,3&%,-6;JST[cfcinru{{~x{}}~‚„ƒ„„‰ˆŠ‡‡„†Œ‰Œ‰…‰‹Œ’‹Ž†‡‹Š‰‘Ž‘‘””’”‘‘“‘‘’ŽŒ‘Œ‘‹‹Š‹‹ŒŽˆŒŒŒŒ‰‹’‘‹—‘“•“—˜›œ™™œ ¡¡Ÿ¥¥¨¨¬©­¯­¯®²µ¶·¸»¿ÁÀÁÃÇÈÇÈÌÐÍÐÎÐÐÒÓÓÓÒÒÓÕÖרÖÕÖÁ†RNQSZcafedfamfgigccdggbge`b]^b_cbbgd]^[^bnƒš«ºÄÆÆÂ¹©–‚bXRHPUMHGF>FMIFKLLGJCKMQW\blsnpsh_`RNLOLG@?A;#,E6#!,/-,&&4403-,1,,,,$,,)(()(6::ADMUMU]`Ybnpsvz{€ƒ~‚}ƒ„€€‚€}ƒƒ†„‡‡ˆ…ƒ…ˆ…‰…†Ž‹ŽˆŒ‰Š‡ŽŠŠ‹ŒŠ‰Ž’“Œ“‘‘‘’‘‘“”‰ŽŒ‘‡ŠŠŒ‹Š‘Ž‹ŒŽŒ“‘”‘–•––”›™ž¡ž  ¢¤¤¤¨¨¬¯±²±±³¶µ¸ººº¿À¿ÃÅÆÇÊÈÌÍÌÎÐÒÓÔÕÓÔÓ×Õ×××ÖÕÑ»qHPSYUZ[\c`aadgnknfkkljjfeeibjiikmpoomekifoŠš•Ž…xgYUTOHKJKOKNPURUOLMKGGOPOSa^fljood[[XFKFEEFB=@AIO`ghskqstywqshebi[TIHDBB><@@B@C9:10,,35=CWQ\Z^]`oz„•ž¨©°¯¯¯©¨šŽƒyiZ`kp{ˆ“›Ÿ£§¬®®±³´µ·µµ¹»·¸µ°ª«£žŽj=@OL5)(2,0,,0'1/7/9//8@;=/694962Ri2,-64.15/>>7ZpK6/(58]bjtvclgK4-/*5C.bp`0$-"(M5A„yALM:=JUmzzc|‘ˆoaL53J(**)+)-()+1-3:))+2)&%'(*947?LLNRVVY]]deciprvvywy||y|}€ƒ€~‚€€|‚„‚…„†„‡‹‰…Žˆ‹‹ŒˆŒ‡‡…‰‹ŒŽŠ‹ŽŽŽ•“ŒŽŽ“’’“•‘”“‘‘ŽŒŠ‰Ž‰’ŽŽŒ‘Ž‘Œ‘”‘’”š–š™—™ž Ÿ Ÿ¡ž¡£¥©§©©®±±²²´º·º¹º»¿¾ÀÃÂÇÆÆÉÌÌÎÏÐÐÕÒÔÕÒÓÖ××ÖרØÖćKMRSTVZb`cfedekgjjnqmhfcbfegmnlttorlojjnlps{}~xofXVVQNQSPNUTTV[ZPTPQMAGITXbenggjhf^[QPJBDAI:<8::DTYRYZZjs}›¥¨®®®¬¬¤žyu]cmrx†™ž¥®°­°³µ²µ²³´··ºº¶®§¦¥¢•o??NL.#-8*.&,2.33-/1.:9=?>4@./D72Sj?(,97/35DFI<`kB5%,03]_ioumdjM6-'"1[ASp~J $,=P4Lrf9BN54Ovj‚dJe}nlg9):)*-3.1+*-,)*$&+%)&&./21;?=?GQST[]WW^cimlxsux|{x{zzy{}~€|€€€ƒƒƒ~…„„…€‡‡……‰‰‹‹ˆ‰ˆ‡‡‹ˆ‰‹ŒŽ’ޑЋ‘“‘’“”•’’–•’Ž•“Œ’‘‰‘ŒŽŒ‘‹Ž””––——•˜—šŸŸŸ ¡¤©¤¤¦¦«­°±±¶¸µ¹»¾»¾½ÁÁÅÂÅÆÉÊÊÍÍÌÏÏÒÔÕÔÔÓÕÔÔÖÖØÖÍžYFWRWV^]Ycbageiknhmlnpjiiifdroqtyqqpnmrrompprvqmhc^ZTUXTRUTU^\VVQVVPNCKFOTX^echkr[fSQINGFCF>C>AAMR\jkjomprw{tvqjmi]YOIEFB>>@9=A=4552///955:BIOPXTYdt}›¢¥®®¯­ª¤šxna`iv|†Œ•Ÿ¦©ª¯±¯²´²¶µ´´µ¸¶³®§©¢¢”n>:GR.+04+4-8JG1,,)826@FD;8>88@?2KwO,'/2*21==FJ[^:4'+,6JYgcl}nyd3'.%.O`Ii‚m1%'1RU@[}]/PN1Hzz_{w?6`vjqL39/-,59;=B99/')((&,()3;=AJEKSNYUVZc`^fosrrux}|zxyxz}y~|€‚~‚…~|‚€€€~‰ˆ‰‚„†…‰Š‰ŒŠ‚ˆ‰†‹‡‹‘’ŠŽŽŽŒŠŒŽ‹—’•””’“Ž‹“–Ž‘‘‘“‰Ž‘Ž‹ŒŒŽŽ’’‘“—˜—–“•šš™œœ››œŸ§¤¡¥©¤ª¬®²°´·µ¹º»º½½¼ÁÀÃÄÅÉËÇÊËÏÎÎÓ×ÕÓÓÓÓÔ×ÔØÕÖЮgHROPS]Z\d``aedmmjrpmqlejklojxqtttxpxqprnnjqpnoifaZVXZZ_[W\db_U\ZXPFJFOMPX]_hnlba[XaSPGOILFOF2290.*-99547*+/28ACIJ9A48@=+?m\6%8.(009?T]_T8/,+'5J^q[K}|xi3$/$ 2Y[`u€N) ';9YJ:xz;1VM;eybz}b8@oqaW66.-+-.1**(%!*&#,,5:9BFILMUXXW^`febjmotsxtvxvxzx{z}|~}„†ƒ|‚|€ƒ~„‰‡‡‡‰†…ˆ‡‰†††…ˆŠ‰‹ŒŽ‹ŒŒŽŒ”’‘”‘‘މ’”“Ž’–’ŽŒ‹‹ŽŽ‹“”•‘–—’“š™œœ›™ŸŸ ¢¥§¨«¨©°¯¯±±¶³¹¼½¾¾½¾¿ÄÄÇÈÅÈÌÍÍÍÏÑÔÓÓÓÒÔÔ×ÕØÕÔÔ½|NNKTWRT\]abbeglkmhsnnokiolsrusttqtorstwqminfghgb``[Z[Yaaa_gb`[VUXNNNRQQVY^gggfcfTO[NOVKKJ@CA?=OT]gilmuwxz}qqjhhebSSEKGB<88=?B=:887;0::;8;:=?INQQRWnu𥬱µ´³°ª¢”†|rfcmo|„Ž™ž¥¨®¯®³´´²¶µ·¶º¸´´®¬§¥†^DdrZ>l~{wO,&) $Emim‹f:"'=NIUu`.5FDHrhpy‚gLSiP]D3%&%+(N8#'$&,.*,45795CFOTV[YXegmnklovruvz|x||}z{}{|{{ƒ‚„‚‚~~|‚‚†ƒƒ…††‰Œ‡‰‰ˆ†‡ˆ†‡Œˆ’‹“Œ‹Ž‹‰ŽŽŽ•‘“’””‘‘’’’‘‘•”‘‰‘ŒŒ’“‘Œ’’”““””–’•™œšœ›™ Ÿ¡¢¥¨¥¤¨©®ª¬­°µ²µ··½»½½½ÁÄÃÂÄÈÊÍÌÎÏÑÕÓÔÓÔÕÓÒÕ×ÖÖÖÆSJHNZX][^db^eggmomspkmmpoptqzuwwrsorslptqnkghhf^\]^[\bfhhedi`XW]TOJUTQTW^bmjc_^XQUKMOJSKN>JHDES]cgztou{wunkjiea^TFHC>=>=;6?;6923:4322C:??@=CMMPL[nvŒœ¦¯³º¿¼º³ª ‹‡{maln…˜ ¥¬¯°´³±´´´±¸¹»ºµ´²¬¦¢œuZG;EH>>34@eQ22.(-*.>Dfb8/(.+'2Ddpj=Jw‚|i6"('%2dme‘mD-$ *>bVtwH#0CHfrkwysiULDIG)(%&+%7.*)(.1<-./58:;DNOVU[cblqpqooopoqvvy{|{€|‚€|€‚~~ƒ‚}~‚‚}€ƒ‰ˆ††ˆ‡‹“ˆ‰ŒˆŠˆ‡Š‰‰ŠŽŽŒŒŒ‘’–‘‘’’‘–“““•‘’–’š“‘’‘”ŽŒŽ’’Ž’–’““”“™–”—™šš›šž £¤§£§©¥©ª­­±³±²±·º»¼¼½ÀÃÄÄÃÆÇÊÌÊÏÏÔÓÑÑÒÑÐÓ×ÕÖØÔÍVFIRSZ\]]ccdghhqnolkntpvrssvuyz{xtqttqqvmkjekhfhebba^gjmjjgcb_XWRMRTTVV^acdaVOVPRLEEKLNDQKILBOTYbjqtqvzzyukkom`^SMFH<:9<7A96342835.-55::DA;CHJTgtžª¸½ÃÃÀÁ½¶¬–‡uhbju}†šž¤ª¬±³³²³»µ´¸¹¶¹º³µ°¦—uNF:.AI@=46*1064,41/+<817-;;1-7>;--0-0:36@C579948>C=20MeU2(-06(=W~o;D&41.):hjxC0TŠƒƒf;83('QrdxwGM="%#/DSJ†W+$*6VS^kykA1-'=4(,4((3&54*-/232/:=NQT[]ddfnmmqtswsrrtvvxz}~~}|}|zƒ~€†€|ƒ†€‚€ƒƒ€‚ˆˆ…ˆ‹Š„†ˆŠƒˆŽ‹ˆŠˆ‰Œ‹ŠŠ†ˆ‹Ž‘ŽŽ•’‘Ž’Ž’‘Ž””•“‘“’”“’‘‘“Ž‘‘˜‘’““”“””˜••—––•›™ŸŸœ¡£¢¤¥§§§§«©°°±¶´µ·µº»º¼¹¾ÁÃÈÆÈÈÉÊÉÎÎÎÒÐÑÒÑÓÐÓÖÖÔÔÒ¾xFCMVQUb]^fikjjinpnnnrr~vzxwtyytpuuupvqqqplokggdbbcdmppqrjkd^\[ZTVQ\[Zbfbc]\VULLDB@JHLFCQBHOMWejpqxrv¥swuqibaU[VSFF>?<6;;9B86888275322486527E>AB@H`y«»ÆÈÎËÊÉÆÇ½°œid{r{†‘›Ÿ¦©°°±²³´¸²µ¶º¸¹¹¶±¥™whRJ:6??8132/'+:5-(7.-438EA:339=>;?>0+AVi9*)+),>k‰oC2#//14FumyD4@w…}T?7*-HUee‚OIO0 !#<69:3:>1473838A@008?=3271+51..+0-7/7==5;9499IA>34:FiM&&+-1FscB1)245*?uh}K?@fŽŠ…m>2+$%9QhuY-MB$!$0VPAl†e8%*;WDNi|oaN*5S2%- +($)1137079=KNXW`^fgflprruuswtusttuyx}~x}~}}z}||‚~ƒ|ƒ€‚‡‡‚‚„ƒƒƒƒ†…І„‡‹‰‹‹‡Šˆ‡‡ŒŠ‡‰‰‰ŠŒŒ’“‘‹““–’•’“—“’”“™“‘‘•“”““—”“’Ž–“‘“—–”‘”–•™——•–š™œ£›ŸŸ£¥¤¤¥¨®©©«°³²µ¶²²·¹¼¹º»¿ÄÅÉÈÅÇÌÉÌÌÍÏÏÏÒÑÑÐÔÑ×ÖÕÓË¡TMMPPUXV\[VVZfnmkfliswxz{w{{wtuxuvptssstlskkkjkhnnqusuuqpjjc^^XVX]b``cfdeWUSODDJCCPBAHKFGQ_Zgjnuz{zzvxolmi^[ZNOFI:@4;8?:9736<997;FR116781046;;>=Hi|‘·ÄÆÉÌÌËÊÈÈÄ·«™sitp|…Œ˜ ¤¨­°²®¯°¶´³´²·´´¯³¯§–tmNE>52(2=N9);03(14*@C646;4??@C;BN9)34Z_%'/-.RƒŠVF1+14/)Gsk‚ABIS…‘‰uN,+(',>amf.561.1?;DK\00515935;A8::Jh‚›ÀÃÅÇËÌÊÌÉÇû²¥{fss‚‘—£§¬±¯°³°¶³³²¸¶¶³²´ª©”{…wVG97//.9A007?1/.@1RB:<79876A?<@?C(01Tq<(2()TŒ‚P?3,0*1-Tvp~9AWKh†“…T/)&)*2RduL+;M>((;QWBHnp;#3_IGXyj>`spc+#&#$)+,34484148:9;K93=GFGNV//7527438<76@INQ\]Xccjjgekkwpmrxwuysy}uwzz{|‚~}}~|€‚„‡…€€…†ƒ…€ƒ‚„‚†‰†„ˆŠ‡Š††„††‹ŽŠŠŠŠŽŒ‰ŠŽŽ‘Ž”‘‘‘”‘““’“–•’’’‘’˜–—•––——‘•”••‘‘‘˜——”™›››šš™”™ ¢¡Ÿ¤§¦¦§¦¥§§¬«²°®®³´¸¸»ºº»¾¿ÁÃÅÆÉÄÉÈÍÏÏÎÏÑÑÒÑÒÒÒÕÒÕÓÆ‹LOQEVVYVZkdffadjhhqvtwyxuuuyv{x~xswutslqrurpuxy~‚wvpkia^^`^^cghbZ\USXSZVWNXUIONEMMNPYbhpw{~€y}vvikicWXQR@<942/,/33483E;=HNRR[^00946629?37:@FhŸÄÃÅÇÆÉÍËÇÅÁ½¸«ƒimu~…‘–¢£¨®°³±±±²´±µ´·´¸²­®¥‚y~f]34221,/8+)6;5)K624=<-:A6A;KXEUCQB:8:ALH8)4gŠYKK7.03:?Pjjz&3_MSE†š€^2+%09W{d€P3.IG87;][>A?Y4'6MOQp|HT`vˆvP/"'%&+*17:>INQYV[_affjkiqvtttsy~wwxyuxxw{z€‚€~~{€|~†„„‚ƒƒƒ††‚…€}„ˆ†ˆŠ‹‡Œ†ˆ‡Š††‡ŠŒŠŠ‰‹Š‹Œ‘Ž“‘’‘–“””’Œ—“““’“–š•“•™”‘š’•–‘”’”–˜•–œ››œšœš›šž¢¡¡¥¤¦¢¤Ÿ¢¨¨¨¬±±­®³²¹¹»¹¸¾½ÀÁÂÄÃÈÉÉÈÊÍÍÍÎÓÐÑÓÓÒÓÓÓÔÔÍ ]EOMPRUSWY^chcdjhkqspswzvzw{{}‚{uztuusqtvwxxsu{€~~ƒyurjh]^\]Y__c]WYWWMOVOS[UNWQKSJKOYZ_huu{„}‚|uonja^YOJF@;A7,,/;777:CB:BKNaZX_660/55278<368@`xŸÀÃÂÃÉÈÈÈÆÂýµ®„isuxˆ’—Ÿ¡©®²±¯±²µ¶²³µµ·»²«¯™wihWW54870)5+-+28;0*8:<9C5:H:@>MSIYHOG117>AQ=1<…ŒM.35>>Lory)+V^JHqsC,#0;Q„d~e4333DG9N_[AA01D?9Qwvw+)K^KJLg{†f=),:Nƒx{i80'#(:GKQY]dfb`++2/6/21<4-68@Rp–ÀÂÅÄÈÈÆÆÆÂ½¹´§€imxƒ‰š¡¢©ª²±µ³µ´´¶µ¶µ·µ³°®¢xIWZ?/9G=0&.)+/?A=)(6M25A9EF8A?MG[DSK?CA.)4,9SoE9LPlmdsZ793=ORQNUYSXZecjhinqpuruwvsw{}zz}||~|~{{~}|„~~‚}‚‚}€‚„…‡‚ƒ€ƒ„„……ˆ†ŠŒŽ„…„…Š…‡‰‡‹ŒŠˆŠŒ‘‰ŽŽ‘’‘”’‘”ޑދޖ—’’”’•–•“š”–”•“”˜–˜”“”—––™˜’––œœ ››žœž¢ £¢¦§¨¦¤¤¨§ªª¬­¬®®´´·¶»¾½ÁÀÁÂÃÄÃÉÇÈÌÊÌÌÍÏÓÔÔÐÑÔÓÔÖÕÅŠe\`]SXY^^\UX\daeolhnmwqu|~……‚…~|x|yz{zxx~„‚ˆ†Ž†‰€}}srid`\[bYXQWRLOHKKOW]_`UYZTUQUOT\^fjtv}ƒ„}€wui`XONF?:524.-1-04.45;FEOQ\adhdb,,736/23:3.255Om‘¸ÃÄÄÈÆÆÇÂÁÀ·²Ÿxinwƒ‡Ž›Ÿ¤£§°®¶´³´µ²¶´·¶·±³¯£N_^E85G0+'+''6>F<"&6I+/KG?U8A?JF9FHETA95:IhaF‡ \:56LC%3;:Hrrq2'0QNKDH?vŠv];1?~“]U-$252(02EBOSCAG/(654Jt`-)>^eIRyZ:9KSQRRT^Z[Z^fdkklnnpqpyvzv}yyx~{}z|~}ƒ‚‚‚~}€~}€~‚„†„ƒ†„‡‚…ƒ‚ƒ„ƒ‡‡‰‰‹††‰…„„І‰†‡Š‰ŒŒŠŽŒ‹‹’’”‘Ž•’“’’’““–”—•™••”””—”—•˜˜“–˜–™•›Ÿœ¤˜œž›ž¤Ÿ£¥¨¥£¦£¨©©©¬¬®°­³¸¸¹¸ºÀ¿ÀÀÃÂÂÁÅÇÇËÊËÌÎÏÏÑÑÏÐÔÔÒÔÔÌvmpf_^X][[Y\a^abkhjifx|~‚†ˆ…„~€y}~~y}€„ŠŠ‹Œ‡‡‚|wvld_V\ZYURNHMJHNEQVY[Z]Y\ZTWSPTY`^fpw~€{‚|{wqe`TK><;:933,)*2.46;DHLS^\aidhil,,736/23:3.255Om‘¸ÃÄÄÈÆÆÇÂÁÀ·²Ÿxinwƒ‡Ž›Ÿ¤£§°®¶´³´µ²¶´·¶·±³¯£N_^E85G0+'+''6>F<"&6I+/KG?U8A?JF9FHETA95:IhaF‡ \:56LC%3;:Hrrq2'0QNKDH?vŠv];1?~“]U-$252(02EBOSCAG/(654Jt`-)>^eIRyZ:9KSQRRT^Z[Z^fdkklnnpqpyvzv}yyx~{}z|~}ƒ‚‚‚~}€~}€~‚„†„ƒ†„‡‚…ƒ‚ƒ„ƒ‡‡‰‰‹††‰…„„І‰†‡Š‰ŒŒŠŽŒ‹‹’’”‘Ž•’“’’’““–”—•™••”””—”—•˜˜“–˜–™•›Ÿœ¤˜œž›ž¤Ÿ£¥¨¥£¦£¨©©©¬¬®°­³¸¸¹¸ºÀ¿ÀÀÃÂÂÁÅÇÇËÊËÌÎÏÏÑÑÏÐÔÔÒÔÔÌvmpf_^X][[Y\a^abkhjifx|~‚†ˆ…„~€y}~~y}€„ŠŠ‹Œ‡‡‚|wvld_V\ZYURNHMJHNEQVY[Z]Y\ZTWSPTY`^fpw~€{‚|{wqe`TK><;:933,)*2.46;DHLS^\aidhil \ No newline at end of file diff --git a/test/testroutines.py b/test/testroutines.py deleted file mode 100644 index 64e56e3..0000000 --- a/test/testroutines.py +++ /dev/null @@ -1,44 +0,0 @@ -import numpy as np -#from PIL import Image - -""" -class TiffReader(object): - def imread(self, filename): - return np.asarray(Image.open(filename)) -""" - -class BinReader(object): - def imread(self, filename): - w, h = 512, 512 - with open(filename, mode='rb') as f: - return np.fromfile(f,dtype=np.uint8,count=w*h).reshape(h,w) - -############################################################################### -def printParametersToString(pars): - txt = r'' - for key, value in pars.items(): - if key == 'algorithm': - txt += "{0} = {1}".format(key, value.__name__) - elif key == 'input': - txt += "{0} = {1}".format(key, np.shape(value)) - elif key == 'refdata': - txt += "{0} = {1}".format(key, np.shape(value)) - else: - txt += "{0} = {1}".format(key, value) - txt += '\n' - return txt - - -def nrmse(im1, im2): - rmse = np.sqrt(np.sum((im2 - im1) ** 2) / float(im1.size)) - max_val = max(np.max(im1), np.max(im2)) - min_val = min(np.min(im1), np.min(im2)) - return 1 - (rmse / (max_val - min_val)) - - -def rmse(im1, im2): - rmse = np.sqrt(np.sum((im1 - im2) ** 2) / float(im1.size)) - return rmse - - -############################################################################### From b6c2757389b3664fb5c74a80d051ae2e0d3091bd Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 14:26:27 +0100 Subject: [PATCH 05/15] adding pytest dependency to build --- recipe/meta.yaml | 1 + test/test_2d_cpu_vs_gpu.py | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/recipe/meta.yaml b/recipe/meta.yaml index dcb2530..b9fbf47 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -26,6 +26,7 @@ requirements: - pip - vc 14 # [win] - cmake + - pytest run: - {{ pin_compatible('numpy', min_pin='x.x', max_pin='x.x') }} diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index 404f493..ee04db3 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -191,6 +191,53 @@ def test_FGP_TV_CPU_vs_GPU_nonsquare( assert fgp_gpu.dtype == np.float32 +def test_PD_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + pars = { + "algorithm": PD_TV, + "input": host_pepper_im_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 1500, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + "lipschitz_const": 8, + } + + print("#############PD TV CPU####################") + pd_cpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, pd_cpu) + print("##############PD TV GPU##################") + pd_gpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, pd_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert pd_cpu.dtype == np.float32 + assert pd_gpu.dtype == np.float32 + + # def test_PD_TV_CPU_vs_GPU(self): # u0, u_ref, Im = self._initiate_data() From 4aaba2aa4bfa916a08a0f65956da2b9288f24855 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 14:30:46 +0100 Subject: [PATCH 06/15] adding pytest to pyproject --- src/Python/pyproject.toml | 6 ++++- test/test_2d_cpu_vs_gpu.py | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/Python/pyproject.toml b/src/Python/pyproject.toml index 911f469..3ff4481 100644 --- a/src/Python/pyproject.toml +++ b/src/Python/pyproject.toml @@ -9,6 +9,10 @@ include = ["ccpi", "ccpi.*"] [project] version = "24.0.1" name = "ccpi-regulariser" -dependencies = ["numpy"] +dependencies = [ + "numpy", + "pillow", + "pytest" +] [project.optional-dependencies] gpu = ["cupy"] diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index ee04db3..c30bf92 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -238,6 +238,55 @@ def test_PD_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): assert pd_gpu.dtype == np.float32 +def test_PD_TV_CPU_vs_GPU_nonsqure( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + pars = { + "algorithm": PD_TV, + "input": host_pepper_im_noise_nonsquare, + "regularisation_parameter": 0.02, + "number_of_iterations": 1500, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + "lipschitz_const": 8, + } + + print("#############PD TV CPU####################") + pd_cpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, pd_cpu) + print("##############PD TV GPU##################") + pd_gpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, pd_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert pd_cpu.dtype == np.float32 + assert pd_gpu.dtype == np.float32 + + # def test_PD_TV_CPU_vs_GPU(self): # u0, u_ref, Im = self._initiate_data() From 22c7931d489adcc1280b7ae554dcfb6c32aac2a2 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 14:45:51 +0100 Subject: [PATCH 07/15] adding imageio to pyproject --- src/Python/pyproject.toml | 3 +- test/test_2d_cpu_vs_gpu.py | 436 ++++++++++++++++++++++--------------- 2 files changed, 261 insertions(+), 178 deletions(-) diff --git a/src/Python/pyproject.toml b/src/Python/pyproject.toml index 3ff4481..a3fd26c 100644 --- a/src/Python/pyproject.toml +++ b/src/Python/pyproject.toml @@ -12,7 +12,8 @@ name = "ccpi-regulariser" dependencies = [ "numpy", "pillow", - "pytest" + "pytest", + "imageio", ] [project.optional-dependencies] gpu = ["cupy"] diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index c30bf92..b6fa725 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -191,6 +191,9 @@ def test_FGP_TV_CPU_vs_GPU_nonsquare( assert fgp_gpu.dtype == np.float32 +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + def test_PD_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): pars = { "algorithm": PD_TV, @@ -287,199 +290,278 @@ def test_PD_TV_CPU_vs_GPU_nonsqure( assert pd_gpu.dtype == np.float32 -# def test_PD_TV_CPU_vs_GPU(self): -# u0, u_ref, Im = self._initiate_data() - -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -# print("____________PD-TV bench___________________") -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -# pars = { -# "algorithm": PD_TV, -# "input": u0, -# "regularisation_parameter": 0.02, -# "number_of_iterations": 1500, -# "tolerance_constant": 0.0, -# "methodTV": 0, -# "nonneg": 0, -# "lipschitz_const": 8, -# } - -# print("#############PD TV CPU####################") -# start_time = timeit.default_timer() -# pd_cpu = PD_TV( -# pars["input"], -# pars["regularisation_parameter"], -# pars["number_of_iterations"], -# pars["tolerance_constant"], -# pars["lipschitz_const"], -# pars["methodTV"], -# pars["nonneg"], -# device="cpu", -# ) - -# rms = rmse(Im, pd_cpu) -# pars["rmse"] = rms - -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) - -# print("##############PD TV GPU##################") -# start_time = timeit.default_timer() -# pd_gpu = PD_TV( -# pars["input"], -# pars["regularisation_parameter"], -# pars["number_of_iterations"], -# pars["tolerance_constant"], -# pars["lipschitz_const"], -# pars["methodTV"], -# pars["nonneg"], -# device="gpu", -# ) - -# rms = rmse(Im, pd_gpu) -# pars["rmse"] = rms -# pars["algorithm"] = PD_TV -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# print("--------Compare the results--------") -# tolerance = 1e-05 -# diff_im = np.zeros(np.shape(pd_cpu)) -# diff_im = abs(pd_cpu - pd_gpu) -# diff_im[diff_im > tolerance] = 1 - -# self.assertLessEqual(diff_im.sum(), 1) -# def test_SB_TV_CPU_vs_GPU(self): -# u0, u_ref, Im = self._initiate_data() - -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -# print("____________SB-TV bench___________________") -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +def test_SB_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + pars = { + "algorithm": SB_TV, + "input": host_pepper_im_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 250, + "tolerance_constant": 0.0, + "methodTV": 0, + } + print("#############SB TV CPU####################") + sb_cpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, sb_cpu) + print("##############SB TV GPU##################") + sb_gpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, sb_gpu) -# # set parameters -# pars = { -# "algorithm": SB_TV, -# "input": u0, -# "regularisation_parameter": 0.02, -# "number_of_iterations": 250, -# "tolerance_constant": 0.0, -# "methodTV": 0, -# } + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert sb_cpu.dtype == np.float32 + assert sb_gpu.dtype == np.float32 -# print("#############SB-TV CPU####################") -# start_time = timeit.default_timer() -# sb_cpu = SB_TV( -# pars["input"], -# pars["regularisation_parameter"], -# pars["number_of_iterations"], -# pars["tolerance_constant"], -# pars["methodTV"], -# device="cpu", -# ) -# rms = rmse(Im, sb_cpu) -# pars["rmse"] = rms +def test_SB_TV_CPU_vs_GPU_nonsquare( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + pars = { + "algorithm": SB_TV, + "input": host_pepper_im_noise_nonsquare, + "regularisation_parameter": 0.02, + "number_of_iterations": 250, + "tolerance_constant": 0.0, + "methodTV": 0, + } + print("#############SB TV CPU####################") + sb_cpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, sb_cpu) + print("##############SB TV GPU##################") + sb_gpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, sb_gpu) -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert sb_cpu.dtype == np.float32 + assert sb_gpu.dtype == np.float32 -# print("##############SB TV GPU##################") -# start_time = timeit.default_timer() -# sb_gpu = SB_TV( -# pars["input"], -# pars["regularisation_parameter"], -# pars["number_of_iterations"], -# pars["tolerance_constant"], -# pars["methodTV"], -# device="gpu", -# ) -# rms = rmse(Im, sb_gpu) -# pars["rmse"] = rms -# pars["algorithm"] = SB_TV -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("--------Compare the results--------") -# tolerance = 1e-05 -# diff_im = np.zeros(np.shape(sb_cpu)) -# diff_im = abs(sb_cpu - sb_gpu) -# diff_im[diff_im > tolerance] = 1 -# self.assertLessEqual(diff_im.sum(), 1) +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -# def test_TGV_CPU_vs_GPU(self): -# u0, u_ref, Im = self._initiate_data() -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -# print("____________TGV bench___________________") -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +def test_TGV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + pars = { + "algorithm": TGV, + "input": host_pepper_im_noise, + "regularisation_parameter": 0.02, + "alpha1": 1.0, + "alpha0": 2.0, + "number_of_iterations": 1000, + "LipshitzConstant": 12, + "tolerance_constant": 0.0, + } + print("#############TGV CPU####################") + tgv_cpu = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, tgv_cpu) + print("##############TGV GPU##################") + tgv_gpu = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, tgv_gpu) -# # set parameters -# # set parameters -# pars = { -# "algorithm": TGV, -# "input": u0, -# "regularisation_parameter": 0.02, -# "alpha1": 1.0, -# "alpha0": 2.0, -# "number_of_iterations": 1000, -# "LipshitzConstant": 12, -# "tolerance_constant": 0.0, -# } + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(tgv_cpu), np.max(tgv_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert tgv_cpu.dtype == np.float32 + assert tgv_gpu.dtype == np.float32 + + +# TODO: This test fails! A bug in TGV. +# def test_TGV_CPU_vs_GPU_nonsquare( +# host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +# ): +# pars = { +# "algorithm": TGV, +# "input": host_pepper_im_noise_nonsquare, +# "regularisation_parameter": 0.02, +# "alpha1": 1.0, +# "alpha0": 2.0, +# "number_of_iterations": 1000, +# "LipshitzConstant": 12, +# "tolerance_constant": 0.0, +# } +# print("#############TGV CPU####################") +# tgv_cpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="cpu", +# ) +# rms_cpu = rmse(host_pepper_im_nonsquare, tgv_cpu) +# print("##############TGV GPU##################") +# tgv_gpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="gpu", +# ) +# rms_gpu = rmse(host_pepper_im_nonsquare, tgv_gpu) + +# print("--------Compare the results--------") +# eps = 1e-5 +# assert_allclose(rms_cpu, rms_gpu, rtol=eps) +# assert_allclose(np.max(tgv_cpu), np.max(tgv_gpu), rtol=eps) +# assert rms_cpu > 0.0 +# assert rms_gpu > 0.0 +# assert tgv_cpu.dtype == np.float32 +# assert tgv_gpu.dtype == np.float32 + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_LLT_ROF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + pars = { + "algorithm": LLT_ROF, + "input": host_pepper_im_noise, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.0085, + "number_of_iterations": 1000, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############LLT_ROF CPU####################") + lltrof_cpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, lltrof_cpu) + print("##############LLT_ROF GPU##################") + lltrof_gpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, lltrof_gpu) -# print("#############TGV CPU####################") -# start_time = timeit.default_timer() -# infovector = np.zeros((2,), dtype="float32") -# tgv_cpu = TGV( -# pars["input"], -# pars["regularisation_parameter"], -# pars["alpha1"], -# pars["alpha0"], -# pars["number_of_iterations"], -# pars["LipshitzConstant"], -# pars["tolerance_constant"], -# device="cpu", -# infovector=infovector, -# ) + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert lltrof_cpu.dtype == np.float32 + assert lltrof_gpu.dtype == np.float32 -# rms = rmse(Im, tgv_cpu) -# pars["rmse"] = rms -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) +def test_LLT_ROF_CPU_vs_GPU_nonsquare( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + pars = { + "algorithm": LLT_ROF, + "input": host_pepper_im_noise_nonsquare, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.0085, + "number_of_iterations": 1000, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############LLT_ROF CPU####################") + lltrof_cpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, lltrof_cpu) + print("##############LLT_ROF GPU##################") + lltrof_gpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, lltrof_gpu) -# print("##############TGV GPU##################") -# start_time = timeit.default_timer() -# tgv_gpu = TGV( -# pars["input"], -# pars["regularisation_parameter"], -# pars["alpha1"], -# pars["alpha0"], -# pars["number_of_iterations"], -# pars["LipshitzConstant"], -# pars["tolerance_constant"], -# device="gpu", -# infovector=infovector, -# ) + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert lltrof_cpu.dtype == np.float32 + assert lltrof_gpu.dtype == np.float32 -# rms = rmse(Im, tgv_gpu) -# pars["rmse"] = rms -# pars["algorithm"] = TGV -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("--------Compare the results--------") -# tolerance = 1e-02 -# diff_im = np.zeros(np.shape(tgv_gpu)) -# diff_im = abs(tgv_cpu - tgv_gpu) -# diff_im[diff_im > tolerance] = 1 -# self.assertLessEqual(diff_im.sum(), 1) # def test_LLT_ROF_CPU_vs_GPU(self): # u0, u_ref, Im = self._initiate_data() From 589a4f341d979505e0fcc90e9cbdf0342c87b12c Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 15:35:32 +0100 Subject: [PATCH 08/15] hiding cupy test, 2d tests complete --- demos/demo_cpu_regularisers.py | 2 +- recipe/meta.yaml | 2 + .../regularisers_GPU/cuda_kernels/__init__.py | 31 +- src/Python/ccpi/cuda_kernels/__init__.py | 31 +- test/test_2d_cpu_vs_gpu.py | 568 ++++++++++-------- test/test_cupy_regul.py | 9 +- 6 files changed, 372 insertions(+), 271 deletions(-) diff --git a/demos/demo_cpu_regularisers.py b/demos/demo_cpu_regularisers.py index ee4e5ea..d565c19 100644 --- a/demos/demo_cpu_regularisers.py +++ b/demos/demo_cpu_regularisers.py @@ -175,7 +175,7 @@ def printParametersToString(pars): pars['tolerance_constant'], pars['methodTV'], pars['nonneg'], - pars['lipschitz_const'], device='cpu') + pars['lipschitz_const'],device='cpu') Qtools = QualityTools(Im, pd_cpu) pars['rmse'] = Qtools.rmse() diff --git a/recipe/meta.yaml b/recipe/meta.yaml index b9fbf47..e1a4d45 100644 --- a/recipe/meta.yaml +++ b/recipe/meta.yaml @@ -27,11 +27,13 @@ requirements: - vc 14 # [win] - cmake - pytest + - imageio run: - {{ pin_compatible('numpy', min_pin='x.x', max_pin='x.x') }} - python - pytest + - imageio - vc 14 # [win] - libgcc-ng # [unix] diff --git a/src/Core/regularisers_GPU/cuda_kernels/__init__.py b/src/Core/regularisers_GPU/cuda_kernels/__init__.py index 9928b64..59e9f69 100644 --- a/src/Core/regularisers_GPU/cuda_kernels/__init__.py +++ b/src/Core/regularisers_GPU/cuda_kernels/__init__.py @@ -1,16 +1,35 @@ import os, sys from typing import List, Optional, Tuple -import cupy as cp + +cupy_enabled = True +try: + import cupy as xp + + try: + xp.cuda.Device(0).compute_capability + + except xp.cuda.runtime.CUDARuntimeError: + import numpy as xp + + cupy_enabled = False + +except ImportError: + + import numpy as xp + + cupy_enabled = False def load_cuda_module( - file: str, name_expressions: Optional[List[str]] = None, options: Tuple[str, ...] = tuple() -) -> cp.RawModule: + file: str, + name_expressions: Optional[List[str]] = None, + options: Tuple[str, ...] = tuple(), +) -> xp.RawModule: """Load a CUDA module file, i.e. a .cuh file, from the file system, compile it, and return is as a CuPy RawModule for further processing. """ - dir = os.path.dirname(os.path.abspath(__file__)) + dir = os.path.dirname(os.path.abspath(__file__)) file = os.path.join(dir, file + ".cuh") # insert a preprocessor line directive to assist compiler errors (so line numbers show correctly in output) escaped = file.replace("\\", "\\\\") @@ -18,6 +37,6 @@ def load_cuda_module( with open(file, "r") as f: code += f.read() - return cp.RawModule( + return xp.RawModule( options=("-std=c++11", *options), code=code, name_expressions=name_expressions - ) \ No newline at end of file + ) diff --git a/src/Python/ccpi/cuda_kernels/__init__.py b/src/Python/ccpi/cuda_kernels/__init__.py index 9928b64..59e9f69 100644 --- a/src/Python/ccpi/cuda_kernels/__init__.py +++ b/src/Python/ccpi/cuda_kernels/__init__.py @@ -1,16 +1,35 @@ import os, sys from typing import List, Optional, Tuple -import cupy as cp + +cupy_enabled = True +try: + import cupy as xp + + try: + xp.cuda.Device(0).compute_capability + + except xp.cuda.runtime.CUDARuntimeError: + import numpy as xp + + cupy_enabled = False + +except ImportError: + + import numpy as xp + + cupy_enabled = False def load_cuda_module( - file: str, name_expressions: Optional[List[str]] = None, options: Tuple[str, ...] = tuple() -) -> cp.RawModule: + file: str, + name_expressions: Optional[List[str]] = None, + options: Tuple[str, ...] = tuple(), +) -> xp.RawModule: """Load a CUDA module file, i.e. a .cuh file, from the file system, compile it, and return is as a CuPy RawModule for further processing. """ - dir = os.path.dirname(os.path.abspath(__file__)) + dir = os.path.dirname(os.path.abspath(__file__)) file = os.path.join(dir, file + ".cuh") # insert a preprocessor line directive to assist compiler errors (so line numbers show correctly in output) escaped = file.replace("\\", "\\\\") @@ -18,6 +37,6 @@ def load_cuda_module( with open(file, "r") as f: code += f.read() - return cp.RawModule( + return xp.RawModule( options=("-std=c++11", *options), code=code, name_expressions=name_expressions - ) \ No newline at end of file + ) diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index b6fa725..c750977 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -47,6 +47,9 @@ def test_ROF_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, rof_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 @@ -56,6 +59,7 @@ def test_ROF_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): assert rms_gpu > 0.0 assert rof_cpu.dtype == np.float32 assert rof_gpu.dtype == np.float32 + print("--------Results match--------") def test_ROF_TV_CPU_vs_GPU_nonsquare( @@ -99,6 +103,7 @@ def test_ROF_TV_CPU_vs_GPU_nonsquare( assert rms_gpu > 0.0 assert rof_cpu.dtype == np.float32 assert rof_gpu.dtype == np.float32 + print("--------Results match--------") def test_FGP_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): @@ -143,6 +148,7 @@ def test_FGP_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): assert rms_gpu > 0.0 assert fgp_cpu.dtype == np.float32 assert fgp_gpu.dtype == np.float32 + print("--------Results match--------") def test_FGP_TV_CPU_vs_GPU_nonsquare( @@ -189,6 +195,7 @@ def test_FGP_TV_CPU_vs_GPU_nonsquare( assert rms_gpu > 0.0 assert fgp_cpu.dtype == np.float32 assert fgp_gpu.dtype == np.float32 + print("--------Results match--------") # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -239,6 +246,7 @@ def test_PD_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): assert rms_gpu > 0.0 assert pd_cpu.dtype == np.float32 assert pd_gpu.dtype == np.float32 + print("--------Results match--------") def test_PD_TV_CPU_vs_GPU_nonsqure( @@ -288,6 +296,7 @@ def test_PD_TV_CPU_vs_GPU_nonsqure( assert rms_gpu > 0.0 assert pd_cpu.dtype == np.float32 assert pd_gpu.dtype == np.float32 + print("--------Results match--------") # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -331,6 +340,7 @@ def test_SB_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): assert rms_gpu > 0.0 assert sb_cpu.dtype == np.float32 assert sb_gpu.dtype == np.float32 + print("--------Results match--------") def test_SB_TV_CPU_vs_GPU_nonsquare( @@ -373,6 +383,7 @@ def test_SB_TV_CPU_vs_GPU_nonsquare( assert rms_gpu > 0.0 assert sb_cpu.dtype == np.float32 assert sb_gpu.dtype == np.float32 + print("--------Results match--------") # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -422,6 +433,7 @@ def test_TGV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): assert rms_gpu > 0.0 assert tgv_cpu.dtype == np.float32 assert tgv_gpu.dtype == np.float32 + print("--------Results match--------") # TODO: This test fails! A bug in TGV. @@ -516,6 +528,7 @@ def test_LLT_ROF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): assert rms_gpu > 0.0 assert lltrof_cpu.dtype == np.float32 assert lltrof_gpu.dtype == np.float32 + print("--------Results match--------") def test_LLT_ROF_CPU_vs_GPU_nonsquare( @@ -561,262 +574,303 @@ def test_LLT_ROF_CPU_vs_GPU_nonsquare( assert rms_gpu > 0.0 assert lltrof_cpu.dtype == np.float32 assert lltrof_gpu.dtype == np.float32 + print("--------Results match--------") -# def test_LLT_ROF_CPU_vs_GPU(self): -# u0, u_ref, Im = self._initiate_data() - -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -# print("____________LLT-ROF bench___________________") -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -# # set parameters -# pars = { -# "algorithm": LLT_ROF, -# "input": u0, -# "regularisation_parameterROF": 0.01, -# "regularisation_parameterLLT": 0.0085, -# "number_of_iterations": 1000, -# "time_marching_parameter": 0.0001, -# "tolerance_constant": 0.0, -# } - -# print("#############LLT- ROF CPU####################") -# start_time = timeit.default_timer() -# lltrof_cpu = LLT_ROF( -# pars["input"], -# pars["regularisation_parameterROF"], -# pars["regularisation_parameterLLT"], -# pars["number_of_iterations"], -# pars["time_marching_parameter"], -# pars["tolerance_constant"], -# device="cpu", -# ) - -# rms = rmse(Im, lltrof_cpu) -# pars["rmse"] = rms - -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("#############LLT- ROF GPU####################") -# start_time = timeit.default_timer() -# lltrof_gpu = LLT_ROF( -# pars["input"], -# pars["regularisation_parameterROF"], -# pars["regularisation_parameterLLT"], -# pars["number_of_iterations"], -# pars["time_marching_parameter"], -# pars["tolerance_constant"], -# device="gpu", -# ) - -# rms = rmse(Im, lltrof_gpu) -# pars["rmse"] = rms -# pars["algorithm"] = LLT_ROF -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("--------Compare the results--------") -# tolerance = 1e-05 -# diff_im = np.zeros(np.shape(lltrof_gpu)) -# diff_im = abs(lltrof_cpu - lltrof_gpu) -# diff_im[diff_im > tolerance] = 1 -# self.assertLessEqual(diff_im.sum(), 1) - -# def test_NDF_CPU_vs_GPU(self): -# u0, u_ref, Im = self._initiate_data() - -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -# print("_______________NDF bench___________________") -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -# # set parameters -# pars = { -# "algorithm": NDF, -# "input": u0, -# "regularisation_parameter": 0.02, -# "edge_parameter": 0.017, -# "number_of_iterations": 1500, -# "time_marching_parameter": 0.01, -# "penalty_type": 1, -# "tolerance_constant": 0.0, -# } - -# print("#############NDF CPU####################") -# start_time = timeit.default_timer() -# ndf_cpu = NDF( -# pars["input"], -# pars["regularisation_parameter"], -# pars["edge_parameter"], -# pars["number_of_iterations"], -# pars["time_marching_parameter"], -# pars["penalty_type"], -# pars["tolerance_constant"], -# device="cpu", -# ) - -# rms = rmse(Im, ndf_cpu) -# pars["rmse"] = rms - -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) - -# print("##############NDF GPU##################") -# start_time = timeit.default_timer() -# ndf_gpu = NDF( -# pars["input"], -# pars["regularisation_parameter"], -# pars["edge_parameter"], -# pars["number_of_iterations"], -# pars["time_marching_parameter"], -# pars["penalty_type"], -# pars["tolerance_constant"], -# device="gpu", -# ) - -# rms = rmse(Im, ndf_gpu) -# pars["rmse"] = rms -# pars["algorithm"] = NDF -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("--------Compare the results--------") -# tolerance = 1e-05 -# diff_im = np.zeros(np.shape(ndf_cpu)) -# diff_im = abs(ndf_cpu - ndf_gpu) -# diff_im[diff_im > tolerance] = 1 -# self.assertLessEqual(diff_im.sum(), 1) - -# def test_Diff4th_CPU_vs_GPU(self): -# u0, u_ref, Im = self._initiate_data() - -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -# print("___Anisotropic Diffusion 4th Order (2D)____") -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -# # set parameters -# pars = { -# "algorithm": Diff4th, -# "input": u0, -# "regularisation_parameter": 0.8, -# "edge_parameter": 0.02, -# "number_of_iterations": 1000, -# "time_marching_parameter": 0.0001, -# "tolerance_constant": 0.0, -# } - -# print("#############Diff4th CPU####################") -# start_time = timeit.default_timer() -# diff4th_cpu = Diff4th( -# pars["input"], -# pars["regularisation_parameter"], -# pars["edge_parameter"], -# pars["number_of_iterations"], -# pars["time_marching_parameter"], -# pars["tolerance_constant"], -# device="cpu", -# ) - -# rms = rmse(Im, diff4th_cpu) -# pars["rmse"] = rms - -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("##############Diff4th GPU##################") -# start_time = timeit.default_timer() -# diff4th_gpu = Diff4th( -# pars["input"], -# pars["regularisation_parameter"], -# pars["edge_parameter"], -# pars["number_of_iterations"], -# pars["time_marching_parameter"], -# pars["tolerance_constant"], -# device="gpu", -# ) - -# rms = rmse(Im, diff4th_gpu) -# pars["rmse"] = rms -# pars["algorithm"] = Diff4th -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("--------Compare the results--------") -# tolerance = 1e-05 -# diff_im = np.zeros(np.shape(diff4th_cpu)) -# diff_im = abs(diff4th_cpu - diff4th_gpu) -# diff_im[diff_im > tolerance] = 1 -# self.assertLessEqual(diff_im.sum(), 1) - -# def test_FDGdTV_CPU_vs_GPU(self): -# u0, u_ref, Im = self._initiate_data() - -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -# print("____________FGP-dTV bench___________________") -# print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -# # set parameters -# pars = { -# "algorithm": FGP_dTV, -# "input": u0, -# "refdata": u_ref, -# "regularisation_parameter": 0.02, -# "number_of_iterations": 500, -# "tolerance_constant": 0.0, -# "eta_const": 0.2, -# "methodTV": 0, -# "nonneg": 0, -# } - -# print("#############FGP dTV CPU####################") -# start_time = timeit.default_timer() -# fgp_dtv_cpu = FGP_dTV( -# pars["input"], -# pars["refdata"], -# pars["regularisation_parameter"], -# pars["number_of_iterations"], -# pars["tolerance_constant"], -# pars["eta_const"], -# pars["methodTV"], -# pars["nonneg"], -# device="cpu", -# ) - -# rms = rmse(Im, fgp_dtv_cpu) -# pars["rmse"] = rms - -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("##############FGP dTV GPU##################") -# start_time = timeit.default_timer() -# fgp_dtv_gpu = FGP_dTV( -# pars["input"], -# pars["refdata"], -# pars["regularisation_parameter"], -# pars["number_of_iterations"], -# pars["tolerance_constant"], -# pars["eta_const"], -# pars["methodTV"], -# pars["nonneg"], -# device="gpu", -# ) - -# rms = rmse(Im, fgp_dtv_gpu) -# pars["rmse"] = rms -# pars["algorithm"] = FGP_dTV -# txtstr = printParametersToString(pars) -# txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) -# print(txtstr) -# print("--------Compare the results--------") -# tolerance = 1e-05 -# diff_im = np.zeros(np.shape(fgp_dtv_cpu)) -# diff_im = abs(fgp_dtv_cpu - fgp_dtv_gpu) -# diff_im[diff_im > tolerance] = 1 -# self.assertLessEqual(diff_im.sum(), 1) - - -# if __name__ == "__main__": -# unittest.main() +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_NDF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + pars = { + "algorithm": NDF, + "input": host_pepper_im_noise, + "regularisation_parameter": 0.02, + "edge_parameter": 0.017, + "number_of_iterations": 500, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 0.0, + } + print("#############NDF CPU####################") + ndf_cpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, ndf_cpu) + print("##############NDF GPU##################") + ndf_gpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_gpu = rmse(host_pepper_im, ndf_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert ndf_cpu.dtype == np.float32 + assert ndf_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_NDF_CPU_vs_GPU_nonsquare( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + pars = { + "algorithm": NDF, + "input": host_pepper_im_noise_nonsquare, + "regularisation_parameter": 0.02, + "edge_parameter": 0.017, + "number_of_iterations": 500, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 0.0, + } + print("#############NDF CPU####################") + ndf_cpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, ndf_cpu) + print("##############NDF GPU##################") + ndf_gpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, ndf_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert ndf_cpu.dtype == np.float32 + assert ndf_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_Diff4th_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + pars = { + "algorithm": Diff4th, + "input": host_pepper_im_noise, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 1000, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############Diff4th CPU####################") + diff4th_cpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, diff4th_cpu) + print("##############Diff4th GPU##################") + diff4th_gpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, diff4th_gpu) + + print("--------Compare the results--------") + eps = 1e-4 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert diff4th_cpu.dtype == np.float32 + assert diff4th_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_Diff4th_CPU_vs_GPU_nonsquare( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + pars = { + "algorithm": Diff4th, + "input": host_pepper_im_noise_nonsquare, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 1000, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############Diff4th CPU####################") + diff4th_cpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, diff4th_cpu) + print("##############Diff4th GPU##################") + diff4th_gpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, diff4th_gpu) + + print("--------Compare the results--------") + eps = 1e-4 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert diff4th_cpu.dtype == np.float32 + assert diff4th_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_FGP_dTV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): + # set parameters + pars = { + "algorithm": FGP_dTV, + "input": host_pepper_im_noise, + "refdata": host_pepper_im, + "regularisation_parameter": 0.02, + "number_of_iterations": 500, + "tolerance_constant": 0.0, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, + } + print("#############FGP_dTV CPU####################") + fgp_dtv_cpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im, fgp_dtv_cpu) + print("##############FGP_dTV GPU##################") + fgp_dtv_gpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im, fgp_dtv_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_dtv_cpu.dtype == np.float32 + assert fgp_dtv_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_FGP_dTV_CPU_vs_GPU_nonsquare( + host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +): + # set parameters + pars = { + "algorithm": FGP_dTV, + "input": host_pepper_im_noise_nonsquare, + "refdata": host_pepper_im_nonsquare, + "regularisation_parameter": 0.02, + "number_of_iterations": 500, + "tolerance_constant": 0.0, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, + } + print("#############FGP_dTV CPU####################") + fgp_dtv_cpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_im_nonsquare, fgp_dtv_cpu) + print("##############FGP_dTV GPU##################") + fgp_dtv_gpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_im_nonsquare, fgp_dtv_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_dtv_cpu.dtype == np.float32 + assert fgp_dtv_gpu.dtype == np.float32 + print("--------Results match--------") diff --git a/test/test_cupy_regul.py b/test/test_cupy_regul.py index 5372674..1783538 100644 --- a/test/test_cupy_regul.py +++ b/test/test_cupy_regul.py @@ -1,7 +1,6 @@ import numpy as np import pytest -from ccpi.filters.regularisersCuPy import ROF_TV, PD_TV from numpy.testing import assert_allclose, assert_equal eps = 1e-5 @@ -9,6 +8,8 @@ @pytest.mark.cupy def test_ROF_TV_2d(device_data): + from ccpi.filters.regularisersCuPy import ROF_TV + filtered_data = ROF_TV( device_data[60, :, :], regularisation_parameter=0.06, @@ -26,6 +27,8 @@ def test_ROF_TV_2d(device_data): @pytest.mark.cupy def test_ROF_TV_3d(device_data): + from ccpi.filters.regularisersCuPy import ROF_TV + filtered_data = ROF_TV( device_data, regularisation_parameter=0.06, @@ -43,6 +46,8 @@ def test_ROF_TV_3d(device_data): @pytest.mark.cupy def test_PD_TV_2d(device_data): + from ccpi.filters.regularisersCuPy import PD_TV + filtered_data = PD_TV( device_data[60, :, :], regularisation_parameter=0.06, @@ -63,6 +68,8 @@ def test_PD_TV_2d(device_data): @pytest.mark.cupy def test_PD_TV_3d(device_data): + from ccpi.filters.regularisersCuPy import PD_TV + filtered_data = PD_TV( device_data, regularisation_parameter=0.06, From 25bcc9d7d259ffe0b68c91e083f3425490354d24 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 15:44:45 +0100 Subject: [PATCH 09/15] CI build correction for pytest --- .github/workflows/build.yml | 2 +- test/test_2d_cpu_vs_gpu.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 912d3a6..db7c5d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: - name: test run: | conda activate "${{ steps.reqs.outputs.envname }}" - PYTHONPATH=./src/Python python -m unittest discover ./test + PYTHONPATH=./src/Python python -m pytest -s ./test - if: always() name: Post Run conda-incubator/setup-miniconda@v3 shell: bash diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index c750977..1917eed 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -139,6 +139,9 @@ def test_FGP_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, fgp_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 @@ -237,6 +240,9 @@ def test_PD_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, pd_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 @@ -331,6 +337,9 @@ def test_SB_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, sb_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 @@ -424,6 +433,9 @@ def test_TGV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, tgv_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 @@ -519,6 +531,9 @@ def test_LLT_ROF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, lltrof_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 @@ -615,6 +630,9 @@ def test_NDF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="cpu", ) rms_gpu = rmse(host_pepper_im, ndf_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 @@ -711,6 +729,9 @@ def test_Diff4th_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, diff4th_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-4 @@ -811,6 +832,9 @@ def test_FGP_dTV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): device="gpu", ) rms_gpu = rmse(host_pepper_im, fgp_dtv_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) print("--------Compare the results--------") eps = 1e-5 From b6613348a17a2797f4698acb863e9da9d506d6f4 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 15:48:59 +0100 Subject: [PATCH 10/15] CI correction 2 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db7c5d9..d1eba27 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,7 +71,7 @@ jobs: cmake --build ./build_proj --target install pip install ./src/Python - name: test - run: PYTHONPATH=./src/Python python -m unittest discover ./test + run: PYTHONPATH=./src/Python python -m pytest -s ./test conda: defaults: {run: {shell: 'bash -el {0}'}} runs-on: ubuntu-latest From 4bd29662ae495dcbfd7be19f77aac48e99e1bd08 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 10 Jun 2024 17:07:52 +0100 Subject: [PATCH 11/15] adding 3d tests --- demos/demo_cpu_regularisers.py | 26 +- test/conftest.py | 42 ++ test/test_2d_cpu_vs_gpu.py | 3 +- test/test_3d_cpu_vs_gpu.py | 906 +++++++++++++++++++++++++++++++++ 4 files changed, 964 insertions(+), 13 deletions(-) create mode 100644 test/test_3d_cpu_vs_gpu.py diff --git a/demos/demo_cpu_regularisers.py b/demos/demo_cpu_regularisers.py index d565c19..9a9f090 100644 --- a/demos/demo_cpu_regularisers.py +++ b/demos/demo_cpu_regularisers.py @@ -169,13 +169,15 @@ def printParametersToString(pars): print ("#############PD TV CPU####################") start_time = timeit.default_timer() -pd_cpu = PD_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'], - pars['lipschitz_const'],device='cpu') +pd_cpu = PD_TV(pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) Qtools = QualityTools(Im, pd_cpu) pars['rmse'] = Qtools.rmse() @@ -208,17 +210,17 @@ def printParametersToString(pars): pars = {'algorithm' : SB_TV, \ 'input' : u0,\ 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1 ,\ + 'number_of_iterations' :100 ,\ 'tolerance_constant':1e-06,\ 'methodTV': 0} print ("#############SB TV CPU####################") start_time = timeit.default_timer() -(sb_cpu,info_vec_cpu) = SB_TV(pars['input'], +sb_cpu = SB_TV(pars['input'], pars['regularisation_parameter'], pars['number_of_iterations'], pars['tolerance_constant'], - pars['methodTV'],'cpu') + pars['methodTV'], device='cpu') #Qtools = QualityTools(Im, sb_cpu) #pars['rmse'] = Qtools.rmse() @@ -259,12 +261,12 @@ def printParametersToString(pars): print ("#############LLT- ROF CPU####################") start_time = timeit.default_timer() -(lltrof_cpu,info_vec_cpu) = LLT_ROF(pars['input'], +lltrof_cpu = LLT_ROF(pars['input'], pars['regularisation_parameterROF'], pars['regularisation_parameterLLT'], pars['number_of_iterations'], pars['time_marching_parameter'], - pars['tolerance_constant'], 'cpu') + pars['tolerance_constant'], device = 'cpu') Qtools = QualityTools(Im, lltrof_cpu) pars['rmse'] = Qtools.rmse() diff --git a/test/conftest.py b/test/conftest.py index 874185b..56e2903 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -97,6 +97,26 @@ def host_pepper_im_noise(host_pepper_im): return u0 +@pytest.fixture(scope="session") +def host_pepper_3d(host_pepper_im): + slices_no = 5 + (x_size, y_size) = np.shape(host_pepper_im) + GT_vol = np.zeros((slices_no, x_size, y_size), dtype="float32") + for i in range(slices_no): + GT_vol[i, :, :] = host_pepper_im + return GT_vol + + +@pytest.fixture(scope="session") +def host_pepper_3d_noise(host_pepper_3d): + perc = 0.075 + u0 = host_pepper_3d + np.random.normal( + loc=0, scale=perc * host_pepper_3d, size=np.shape(host_pepper_3d) + ) + u0 = u0.astype("float32") + return u0 + + @pytest.fixture(scope="session") def host_pepper_im_noise_nonsquare(host_pepper_im_nonsquare): perc = 0.05 @@ -109,6 +129,28 @@ def host_pepper_im_noise_nonsquare(host_pepper_im_nonsquare): return u0 +@pytest.fixture(scope="session") +def host_pepper_3d_noncubic(host_pepper_im_nonsquare): + slices_no = 5 + (x_size, y_size) = np.shape(host_pepper_im_nonsquare) + GT_vol = np.zeros((slices_no, x_size, y_size), dtype="float32") + for i in range(slices_no): + GT_vol[i, :, :] = host_pepper_im_nonsquare + return GT_vol + + +@pytest.fixture(scope="session") +def host_pepper_3d_noise_noncubic(host_pepper_3d_noncubic): + perc = 0.075 + u0 = host_pepper_3d_noncubic + np.random.normal( + loc=0, + scale=perc * host_pepper_3d_noncubic, + size=np.shape(host_pepper_3d_noncubic), + ) + u0 = u0.astype("float32") + return u0 + + @pytest.fixture def device_pepper_im(host_pepper_im, ensure_clean_memory): return xp.ascontiguousarray(xp.asarray(host_pepper_im, order="C"), dtype=np.float32) diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index 1917eed..af23d06 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -106,6 +106,7 @@ def test_ROF_TV_CPU_vs_GPU_nonsquare( print("--------Results match--------") +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% def test_FGP_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): pars = { "algorithm": FGP_TV, @@ -255,7 +256,7 @@ def test_PD_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Results match--------") -def test_PD_TV_CPU_vs_GPU_nonsqure( +def test_PD_TV_CPU_vs_GPU_nonsquare( host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare ): pars = { diff --git a/test/test_3d_cpu_vs_gpu.py b/test/test_3d_cpu_vs_gpu.py new file mode 100644 index 0000000..4fa1315 --- /dev/null +++ b/test/test_3d_cpu_vs_gpu.py @@ -0,0 +1,906 @@ +from numpy._typing._array_like import NDArray +import pytest +import numpy as np +from ccpi.filters.regularisers import ( + ROF_TV, + FGP_TV, + PD_TV, + SB_TV, + TGV, + LLT_ROF, + FGP_dTV, + NDF, + Diff4th, +) +from conftest import rmse, printParametersToString +from numpy.testing import assert_allclose + + +def test_ROF_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + # set parameters + pars = { + "algorithm": ROF_TV, + "input": host_pepper_3d_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 20, + "time_marching_parameter": 0.001, + "tolerance_constant": 0.0, + } + print("#############ROF TV CPU####################") + rof_cpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, rof_cpu) + print("##############ROF TV GPU##################") + rof_gpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d, rof_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert rof_cpu.dtype == np.float32 + assert rof_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_ROF_TV_CPU_vs_GPU_noncubic( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + # set parameters + pars = { + "algorithm": ROF_TV, + "input": host_pepper_3d_noise_noncubic, + "regularisation_parameter": 0.02, + "number_of_iterations": 20, + "time_marching_parameter": 0.001, + "tolerance_constant": 0.0, + } + print("#############ROF TV CPU####################") + rof_cpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, rof_cpu) + print("##############ROF TV GPU##################") + rof_gpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, rof_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert rof_cpu.dtype == np.float32 + assert rof_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +def test_FGP_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + pars = { + "algorithm": FGP_TV, + "input": host_pepper_3d_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 30, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + } + + print("#############FGP TV CPU####################") + fgp_cpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, fgp_cpu) + print("##############FGP TV GPU##################") + fgp_gpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d, fgp_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_cpu.dtype == np.float32 + assert fgp_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_FGP_TV_CPU_vs_GPU_noncubic( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + pars = { + "algorithm": FGP_TV, + "input": host_pepper_3d_noise_noncubic, + "regularisation_parameter": 0.02, + "number_of_iterations": 30, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + } + + print("#############FGP TV CPU####################") + fgp_cpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, fgp_cpu) + print("##############FGP TV GPU##################") + fgp_gpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, fgp_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_cpu.dtype == np.float32 + assert fgp_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_PD_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + pars = { + "algorithm": PD_TV, + "input": host_pepper_3d_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 50, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + "lipschitz_const": 8, + } + + print("#############PD TV CPU####################") + pd_cpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, pd_cpu) + print("##############PD TV GPU##################") + pd_gpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d, pd_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert pd_cpu.dtype == np.float32 + assert pd_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_PD_TV_CPU_vs_GPU_noncubic( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + pars = { + "algorithm": PD_TV, + "input": host_pepper_3d_noise_noncubic, + "regularisation_parameter": 0.02, + "number_of_iterations": 50, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, + "lipschitz_const": 8, + } + + print("#############PD TV CPU####################") + pd_cpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, pd_cpu) + print("##############PD TV GPU##################") + pd_gpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, pd_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert pd_cpu.dtype == np.float32 + assert pd_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_SB_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + pars = { + "algorithm": SB_TV, + "input": host_pepper_3d_noise, + "regularisation_parameter": 0.02, + "number_of_iterations": 50, + "tolerance_constant": 0.0, + "methodTV": 0, + } + print("#############SB TV CPU####################") + sb_cpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, sb_cpu) + print("##############SB TV GPU##################") + sb_gpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d, sb_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert sb_cpu.dtype == np.float32 + assert sb_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_SB_TV_CPU_vs_GPU_noncubic( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + pars = { + "algorithm": SB_TV, + "input": host_pepper_3d_noise_noncubic, + "regularisation_parameter": 0.02, + "number_of_iterations": 50, + "tolerance_constant": 0.0, + "methodTV": 0, + } + print("#############SB TV CPU####################") + sb_cpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, sb_cpu) + print("##############SB TV GPU##################") + sb_gpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, sb_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert sb_cpu.dtype == np.float32 + assert sb_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +# TODO: This test fails! A bug in TGV. +# def test_TGV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): +# pars = { +# "algorithm": TGV, +# "input": host_pepper_3d_noise, +# "regularisation_parameter": 0.02, +# "alpha1": 1.0, +# "alpha0": 2.0, +# "number_of_iterations": 50, +# "LipshitzConstant": 12, +# "tolerance_constant": 0.0, +# } +# print("#############TGV CPU####################") +# tgv_cpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="cpu", +# ) +# rms_cpu = rmse(host_pepper_3d, tgv_cpu) +# print("##############TGV GPU##################") +# tgv_gpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="gpu", +# ) +# rms_gpu = rmse(host_pepper_3d, tgv_gpu) +# pars["rmse"] = rms_gpu +# txtstr = printParametersToString(pars) +# print(txtstr) + +# print("--------Compare the results--------") +# eps = 1e-5 +# assert_allclose(rms_cpu, rms_gpu, rtol=eps) +# assert_allclose(np.max(tgv_cpu), np.max(tgv_gpu), rtol=eps) +# assert rms_cpu > 0.0 +# assert rms_gpu > 0.0 +# assert tgv_cpu.dtype == np.float32 +# assert tgv_gpu.dtype == np.float32 +# print("--------Results match--------") + + +# TODO: This test fails! A bug in TGV. +# def test_TGV_CPU_vs_GPU_nonsquare( +# host_pepper_im_nonsquare, host_pepper_im_noise_nonsquare +# ): +# pars = { +# "algorithm": TGV, +# "input": host_pepper_im_noise_nonsquare, +# "regularisation_parameter": 0.02, +# "alpha1": 1.0, +# "alpha0": 2.0, +# "number_of_iterations": 1000, +# "LipshitzConstant": 12, +# "tolerance_constant": 0.0, +# } +# print("#############TGV CPU####################") +# tgv_cpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="cpu", +# ) +# rms_cpu = rmse(host_pepper_im_nonsquare, tgv_cpu) +# print("##############TGV GPU##################") +# tgv_gpu = TGV( +# pars["input"], +# pars["regularisation_parameter"], +# pars["alpha1"], +# pars["alpha0"], +# pars["number_of_iterations"], +# pars["LipshitzConstant"], +# pars["tolerance_constant"], +# device="gpu", +# ) +# rms_gpu = rmse(host_pepper_im_nonsquare, tgv_gpu) + +# print("--------Compare the results--------") +# eps = 1e-5 +# assert_allclose(rms_cpu, rms_gpu, rtol=eps) +# assert_allclose(np.max(tgv_cpu), np.max(tgv_gpu), rtol=eps) +# assert rms_cpu > 0.0 +# assert rms_gpu > 0.0 +# assert tgv_cpu.dtype == np.float32 +# assert tgv_gpu.dtype == np.float32 + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_LLT_ROF_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + pars = { + "algorithm": LLT_ROF, + "input": host_pepper_3d_noise, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.0085, + "number_of_iterations": 50, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############LLT_ROF CPU####################") + lltrof_cpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, lltrof_cpu) + print("##############LLT_ROF GPU##################") + lltrof_gpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d, lltrof_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert lltrof_cpu.dtype == np.float32 + assert lltrof_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_LLT_ROF_CPU_vs_GPU_noncubic( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + pars = { + "algorithm": LLT_ROF, + "input": host_pepper_3d_noise_noncubic, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.0085, + "number_of_iterations": 50, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############LLT_ROF CPU####################") + lltrof_cpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, lltrof_cpu) + print("##############LLT_ROF GPU##################") + lltrof_gpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, lltrof_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert lltrof_cpu.dtype == np.float32 + assert lltrof_gpu.dtype == np.float32 + print("--------Results match--------") + + # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_NDF_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + pars = { + "algorithm": NDF, + "input": host_pepper_3d_noise, + "regularisation_parameter": 0.02, + "edge_parameter": 0.017, + "number_of_iterations": 50, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 0.0, + } + print("#############NDF CPU####################") + ndf_cpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, ndf_cpu) + print("##############NDF GPU##################") + ndf_gpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_gpu = rmse(host_pepper_3d, ndf_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert ndf_cpu.dtype == np.float32 + assert ndf_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_NDF_CPU_vs_GPU_noncubic( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + pars = { + "algorithm": NDF, + "input": host_pepper_3d_noise_noncubic, + "regularisation_parameter": 0.02, + "edge_parameter": 0.017, + "number_of_iterations": 50, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 0.0, + } + print("#############NDF CPU####################") + ndf_cpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, ndf_cpu) + print("##############NDF GPU##################") + ndf_gpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, ndf_gpu) + + print("--------Compare the results--------") + eps = 1e-4 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert ndf_cpu.dtype == np.float32 + assert ndf_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_Diff4th_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + pars = { + "algorithm": Diff4th, + "input": host_pepper_3d_noise, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 50, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############Diff4th CPU####################") + diff4th_cpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, diff4th_cpu) + print("##############Diff4th GPU##################") + diff4th_gpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d, diff4th_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-4 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert diff4th_cpu.dtype == np.float32 + assert diff4th_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_Diff4th_CPU_vs_GPU_nonsquare( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + pars = { + "algorithm": Diff4th, + "input": host_pepper_3d_noise_noncubic, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 50, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + print("#############Diff4th CPU####################") + diff4th_cpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, diff4th_cpu) + print("##############Diff4th GPU##################") + diff4th_gpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, diff4th_gpu) + + print("--------Compare the results--------") + eps = 1e-4 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert diff4th_cpu.dtype == np.float32 + assert diff4th_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +def test_FGP_dTV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): + # set parameters + pars = { + "algorithm": FGP_dTV, + "input": host_pepper_3d_noise, + "refdata": host_pepper_3d, + "regularisation_parameter": 0.02, + "number_of_iterations": 50, + "tolerance_constant": 0.0, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, + } + print("#############FGP_dTV CPU####################") + fgp_dtv_cpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d, fgp_dtv_cpu) + print("##############FGP_dTV GPU##################") + fgp_dtv_gpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d, fgp_dtv_gpu) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_dtv_cpu.dtype == np.float32 + assert fgp_dtv_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_FGP_dTV_CPU_vs_GPU_nonsquare( + host_pepper_3d_noncubic, host_pepper_3d_noise_noncubic +): + # set parameters + pars = { + "algorithm": FGP_dTV, + "input": host_pepper_3d_noise_noncubic, + "refdata": host_pepper_3d_noncubic, + "regularisation_parameter": 0.02, + "number_of_iterations": 50, + "tolerance_constant": 0.0, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, + } + print("#############FGP_dTV CPU####################") + fgp_dtv_cpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + ) + rms_cpu = rmse(host_pepper_3d_noncubic, fgp_dtv_cpu) + print("##############FGP_dTV GPU##################") + fgp_dtv_gpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + ) + rms_gpu = rmse(host_pepper_3d_noncubic, fgp_dtv_gpu) + + print("--------Compare the results--------") + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) + assert rms_cpu > 0.0 + assert rms_gpu > 0.0 + assert fgp_dtv_cpu.dtype == np.float32 + assert fgp_dtv_gpu.dtype == np.float32 + print("--------Results match--------") From b1a9a3f3cb953b6f6bdaa418fd18ec8c8e07d85d Mon Sep 17 00:00:00 2001 From: dkazanc Date: Mon, 1 Jul 2024 22:16:44 +0100 Subject: [PATCH 12/15] all demos updated --- demos/Matlab_demos/demoMatlab_3Ddenoise.m | 2 +- demos/Matlab_demos/demoMatlab_denoise.m | 2 +- demos/demo_cpu_regularisers3D.py | 37 +- demos/demo_cpu_vs_gpu_regularisers.py | 905 -------------------- demos/demo_gpu_regularisers.py | 43 +- demos/demo_gpu_regularisers3D.py | 43 +- demos/demo_gpu_regularisers3D_CuPy.py | 27 +- src/Python/ccpi/filters/regularisersCuPy.py | 117 ++- test/test_cupy_regul.py | 2 - 9 files changed, 118 insertions(+), 1060 deletions(-) delete mode 100644 demos/demo_cpu_vs_gpu_regularisers.py diff --git a/demos/Matlab_demos/demoMatlab_3Ddenoise.m b/demos/Matlab_demos/demoMatlab_3Ddenoise.m index b7f92cb..cd84372 100644 --- a/demos/Matlab_demos/demoMatlab_3Ddenoise.m +++ b/demos/Matlab_demos/demoMatlab_3Ddenoise.m @@ -14,7 +14,7 @@ slices = 15; vol3D = zeros(N,N,slices, 'single'); Ideal3D = zeros(N,N,slices, 'single'); -Im = double(imread('lena_gray_512.tif'))/255; % loading image +Im = double(imread('peppers.tif'))/255; % loading image for i = 1:slices vol3D(:,:,i) = Im + .05*randn(size(Im)); Ideal3D(:,:,i) = Im; diff --git a/demos/Matlab_demos/demoMatlab_denoise.m b/demos/Matlab_demos/demoMatlab_denoise.m index 3d93cb6..9524d86 100644 --- a/demos/Matlab_demos/demoMatlab_denoise.m +++ b/demos/Matlab_demos/demoMatlab_denoise.m @@ -9,7 +9,7 @@ addpath(Path2); addpath(Path3); -Im = double(imread('lena_gray_512.tif'))/255; % loading image +Im = double(imread('peppers.tif'))/255; % loading image u0 = Im + .05*randn(size(Im)); u0(u0 < 0) = 0; figure; imshow(u0, [0 1]); title('Noisy image'); %% diff --git a/demos/demo_cpu_regularisers3D.py b/demos/demo_cpu_regularisers3D.py index 11b7b0b..203881b 100644 --- a/demos/demo_cpu_regularisers3D.py +++ b/demos/demo_cpu_regularisers3D.py @@ -13,6 +13,8 @@ import numpy as np import os import timeit +from imageio.v2 import imread + from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th from ccpi.supp.qualitymetrics import QualityTools ############################################################################### @@ -31,14 +33,12 @@ def printParametersToString(pars): return txt ############################################################################### -os.chdir(os.path.join("..", "demos")) -filename = os.path.join( "data" ,"lena_gray_512.tif") +filename = os.path.join( "../test/test_data" ,"peppers.tif") # read image -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') +Im = imread(filename) -Im = Im/255 +Im = Im/255.0 perc = 0.05 u0 = Im + np.random.normal(loc = 0 , scale = perc * Im , @@ -47,29 +47,10 @@ def printParametersToString(pars): scale = 0.01 * Im , size = np.shape(Im)) (N,M) = np.shape(u0) -# map the u0 u0->u0>0 -# f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) + u0 = u0.astype('float32') u_ref = u_ref.astype('float32') -# change dims to check that modules work with non-squared images -""" -M = M-100 -u_ref2 = np.zeros([N,M],dtype='float32') -u_ref2[:,0:M] = u_ref[:,0:M] -u_ref = u_ref2 -del u_ref2 - -u02 = np.zeros([N,M],dtype='float32') -u02[:,0:M] = u0[:,0:M] -u0 = u02 -del u02 - -Im2 = np.zeros([N,M],dtype='float32') -Im2[:,0:M] = Im[:,0:M] -Im = Im2 -del Im2 -""" slices = 20 noisyVol = np.zeros((slices,N,M),dtype='float32') @@ -81,6 +62,7 @@ def printParametersToString(pars): noisyRef[i,:,:] = Im + np.random.normal(loc = 0 , scale = 0.01 * Im , size = np.shape(Im)) idealVol[i,:,:] = Im +info_vec_cpu = np.zeros(2, dtype = np.float32) #%% print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") print ("_______________ROF-TV (3D)_________________") @@ -97,7 +79,7 @@ def printParametersToString(pars): # set parameters pars = {'algorithm': ROF_TV, \ 'input' : noisyVol,\ - 'regularisation_parameter':0.02 * 100,\ + 'regularisation_parameter':0.08,\ 'number_of_iterations': 200,\ 'time_marching_parameter': 0.0007,\ 'tolerance_constant':1e-06} @@ -109,7 +91,7 @@ def printParametersToString(pars): pars['regularisation_parameter'], pars['number_of_iterations'], pars['time_marching_parameter'], - pars['tolerance_constant'], device='gpu', infovector=info_vec_cpu) + pars['tolerance_constant'], device='cpu', infovector=info_vec_cpu) Qtools = QualityTools(idealVol, rof_cpu3D) pars['rmse'] = Qtools.rmse() @@ -364,7 +346,6 @@ def printParametersToString(pars): verticalalignment='top', bbox=props) imgplot = plt.imshow(tgv_cpu3D[10,:,:], cmap="gray") plt.title('{}'.format('Recovered volume on the CPU using TGV')) - #%% print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") print ("________________NDF (3D)___________________") diff --git a/demos/demo_cpu_vs_gpu_regularisers.py b/demos/demo_cpu_vs_gpu_regularisers.py deleted file mode 100644 index a63efd2..0000000 --- a/demos/demo_cpu_vs_gpu_regularisers.py +++ /dev/null @@ -1,905 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu Feb 22 11:39:43 2018 - -Demonstration of CPU implementation against the GPU one - -@authors: Daniil Kazantsev, Edoardo Pasca -""" - -#%% -import matplotlib.pyplot as plt -import numpy as np -import os -import timeit -from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th -from ccpi.filters.regularisers import PatchSelect -from ccpi.supp.qualitymetrics import QualityTools -############################################################################### -def printParametersToString(pars): - txt = r'' - for key, value in pars.items(): - if key== 'algorithm' : - txt += "{0} = {1}".format(key, value.__name__) - elif key == 'input': - txt += "{0} = {1}".format(key, np.shape(value)) - elif key == 'refdata': - txt += "{0} = {1}".format(key, np.shape(value)) - else: - txt += "{0} = {1}".format(key, value) - txt += '\n' - return txt -############################################################################### - - -os.chdir(os.path.join("..", "demos")) -filename = os.path.join("data" ,"lena_gray_512.tif") - -# read image -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') - -Im = Im/255 -perc = 0.05 -u0 = Im + np.random.normal(loc = 0 , - scale = perc * Im , - size = np.shape(Im)) -u_ref = Im + np.random.normal(loc = 0 , - scale = 0.01 * Im , - size = np.shape(Im)) - -# map the u0 u0->u0>0 -# f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) -u0 = u0.astype('float32') -u_ref = u_ref.astype('float32') - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________ROF-TV bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of ROF-TV regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm': ROF_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02,\ - 'number_of_iterations': 1000,\ - 'time_marching_parameter': 0.001,\ - 'tolerance_constant':0.0} - -print ("#############ROF TV CPU####################") -start_time = timeit.default_timer() -infocpu = np.zeros(2, dtype='float32') -rof_cpu = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, rof_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(rof_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - -#%% -print ("##############ROF TV GPU##################") -start_time = timeit.default_timer() -infogpu = np.zeros(2, dtype='float32') -rof_gpu = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, rof_gpu) -pars['rmse'] = Qtools.rmse() - -pars['algorithm'] = ROF_TV -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(rof_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - -#%% -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(rof_cpu)) -diff_im = abs(rof_cpu - rof_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________FGP-TV bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of FGP-TV regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : FGP_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :400 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP TV CPU####################") -start_time = timeit.default_timer() -fgp_cpu = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'],device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, fgp_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - - -print ("##############FGP TV GPU##################") -start_time = timeit.default_timer() -fgp_gpu = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'],device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, fgp_gpu) -pars['rmse'] = Qtools.rmse() - -pars['algorithm'] = FGP_TV -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(fgp_cpu)) -diff_im = abs(fgp_cpu - fgp_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") - - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________PD-TV bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of PD-TV regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : PD_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1500 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0 ,\ - 'nonneg': 0, - 'lipschitz_const' : 8} - -print ("#############PD TV CPU####################") -start_time = timeit.default_timer() -pd_cpu = PD_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['lipschitz_const'], - pars['methodTV'], - pars['nonneg'], - device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, pd_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(pd_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - -# set parameters -pars = {'algorithm' : PD_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1500 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0 ,\ - 'nonneg': 0, - 'lipschitz_const' : 8} - -print ("#############PD TV GPU####################") -start_time = timeit.default_timer() -pd_gpu = PD_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['lipschitz_const'], - pars['methodTV'], - pars['nonneg'], - device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, pd_gpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(pd_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(pd_cpu)) -diff_im = abs(pd_cpu - pd_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________SB-TV bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of SB-TV regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : SB_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :250 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0} - -print ("#############SB-TV CPU####################") -start_time = timeit.default_timer() -sb_cpu = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], device='cpu', infovector=infocpu) - - -Qtools = QualityTools(Im, sb_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(sb_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - - -print ("##############SB TV GPU##################") -start_time = timeit.default_timer() -sb_gpu = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, sb_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = SB_TV -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(sb_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(sb_cpu)) -diff_im = abs(sb_cpu - sb_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________LLT-ROF bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of LLT-ROF regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : LLT_ROF, \ - 'input' : u0,\ - 'regularisation_parameterROF':0.01, \ - 'regularisation_parameterLLT':0.0085, \ - 'number_of_iterations' : 1000 ,\ - 'time_marching_parameter' :0.0001 ,\ - 'tolerance_constant':0.0} - - -print ("#############LLT- ROF CPU####################") -start_time = timeit.default_timer() -lltrof_cpu = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, lltrof_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(lltrof_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - -print ("#############LLT- ROF GPU####################") -start_time = timeit.default_timer() -lltrof_gpu = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, lltrof_gpu) -pars['rmse'] = Qtools.rmse() - -pars['algorithm'] = LLT_ROF -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(lltrof_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(lltrof_gpu)) -diff_im = abs(lltrof_cpu - lltrof_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________TGV bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of TGV regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : TGV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'alpha1':1.0,\ - 'alpha0':2.0,\ - 'number_of_iterations' :1000 ,\ - 'LipshitzConstant' :12 ,\ - 'tolerance_constant':0.0} - -print ("#############TGV CPU####################") -start_time = timeit.default_timer() -tgv_cpu = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'],device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, tgv_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(tgv_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - -print ("##############TGV GPU##################") -start_time = timeit.default_timer() -tgv_gpu = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'],device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, tgv_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = TGV -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(tgv_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - -print ("--------Compare the results--------") -tolerance = 1e-02 -diff_im = np.zeros(np.shape(tgv_gpu)) -diff_im = abs(tgv_cpu - tgv_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print (f"Arrays do not match! {diff_im.sum()}") - plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -else: - print (f"Arrays match {diff_im.sum()}") - -N =10 -diff = tgv_cpu[:N,:N] - tgv_gpu[:N,:N] -lim = np.max(np.abs(diff)) -plt.imshow(diff, vmin=-lim, vmax=lim, cmap="seismic") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________NDF bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of NDF regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : NDF, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'edge_parameter':0.017,\ - 'number_of_iterations' :1500 ,\ - 'time_marching_parameter':0.01,\ - 'penalty_type':1,\ - 'tolerance_constant':0.0} - -print ("#############NDF CPU####################") -start_time = timeit.default_timer() -ndf_cpu = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'],device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, ndf_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(ndf_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - - -print ("##############NDF GPU##################") -start_time = timeit.default_timer() -infogpu = np.zeros(2, dtype='float32') -ndf_gpu = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'],device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, ndf_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = NDF -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(ndf_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(ndf_cpu)) -diff_im = abs(ndf_cpu - ndf_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Anisotropic Diffusion 4th Order (2D)____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of Diff4th regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : Diff4th, \ - 'input' : u0,\ - 'regularisation_parameter':0.8, \ - 'edge_parameter':0.02,\ - 'number_of_iterations' :1500 ,\ - 'time_marching_parameter':0.001,\ - 'tolerance_constant':0.0} - -print ("#############Diff4th CPU####################") -start_time = timeit.default_timer() -diff4th_cpu = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, diff4th_cpu) -pars['rmse'] = Qtools.rmse() - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(diff4th_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - -print ("##############Diff4th GPU##################") -start_time = timeit.default_timer() -diff4th_gpu = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],device='gpu', infovector=infogpu) - -Qtools = QualityTools(Im, diff4th_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = Diff4th -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(diff4th_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(diff4th_cpu)) -diff_im = abs(diff4th_cpu - diff4th_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________FGP-dTV bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of FGP-dTV regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,4,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -# set parameters -pars = {'algorithm' : FGP_dTV, \ - 'input' : u0,\ - 'refdata' : u_ref,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :500 ,\ - 'tolerance_constant':0.0,\ - 'eta_const':0.2,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP dTV CPU####################") -start_time = timeit.default_timer() -fgp_dtv_cpu = FGP_dTV(pars['input'], - pars['refdata'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['eta_const'], - pars['methodTV'], - pars['nonneg'],device='cpu', infovector=infocpu) - -Qtools = QualityTools(Im, fgp_dtv_cpu) -pars['rmse'] = Qtools.rmse() - - -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,2) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_dtv_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) - -print ("##############FGP dTV GPU##################") -start_time = timeit.default_timer() -fgp_dtv_gpu = FGP_dTV(pars['input'], - pars['refdata'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['eta_const'], - pars['methodTV'], - pars['nonneg'],device='cpu', infovector=infogpu) -Qtools = QualityTools(Im, fgp_dtv_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = FGP_dTV -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,4,3) - -# these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) -# place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_dtv_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) - - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(fgp_dtv_cpu)) -diff_im = abs(fgp_dtv_cpu - fgp_dtv_gpu) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,4,4) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____Non-local regularisation bench_________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot -fig = plt.figure() -plt.suptitle('Comparison of Nonlocal TV regulariser using CPU and GPU implementations') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -pars = {'algorithm' : PatchSelect, \ - 'input' : u0,\ - 'searchwindow': 7, \ - 'patchwindow': 2,\ - 'neighbours' : 15 ,\ - 'edge_parameter':0.18} - -print ("############## Nonlocal Patches on CPU##################") -start_time = timeit.default_timer() -H_i, H_j, WeightsCPU = PatchSelect(pars['input'], - pars['searchwindow'], - pars['patchwindow'], - pars['neighbours'], - pars['edge_parameter'], device='cpu') -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -#%% -print ("############## Nonlocal Patches on GPU##################") -start_time = timeit.default_timer() -start_time = timeit.default_timer() -H_i, H_j, WeightsGPU = PatchSelect(pars['input'], - pars['searchwindow'], - pars['patchwindow'], - pars['neighbours'], - pars['edge_parameter'],device='gpu') -txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) - -print ("--------Compare the results--------") -tolerance = 1e-05 -diff_im = np.zeros(np.shape(u0)) -diff_im = abs(WeightsCPU[0,:,:] - WeightsGPU[0,:,:]) -diff_im[diff_im > tolerance] = 1 -a=fig.add_subplot(1,2,2) -imgplot = plt.imshow(diff_im, vmin=0, vmax=1, cmap="gray") -plt.title('{}'.format('Pixels larger threshold difference')) -if (diff_im.sum() > 1): - print ("Arrays do not match!") -else: - print ("Arrays match") -#%% \ No newline at end of file diff --git a/demos/demo_gpu_regularisers.py b/demos/demo_gpu_regularisers.py index f3874e7..a93e3c3 100644 --- a/demos/demo_gpu_regularisers.py +++ b/demos/demo_gpu_regularisers.py @@ -12,6 +12,8 @@ import numpy as np import os import timeit +from imageio.v2 import imread + from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th from ccpi.filters.regularisers import PatchSelect, NLTV from ccpi.supp.qualitymetrics import QualityTools @@ -30,16 +32,14 @@ def printParametersToString(pars): txt += '\n' return txt ############################################################################### -#%% -os.chdir(os.path.join("..", "demos")) -filename = os.path.join( "data" ,"lena_gray_512.tif") + +filename = os.path.join( "../test/test_data" ,"peppers.tif") # read image -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') +Im = imread(filename) -Im = Im/255 -perc = 0.05 +Im = Im/255.0 +perc = 0.08 u0 = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) @@ -51,23 +51,10 @@ def printParametersToString(pars): # f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) u0 = u0.astype('float32') u_ref = u_ref.astype('float32') -""" -M = M-100 -u_ref2 = np.zeros([N,M],dtype='float32') -u_ref2[:,0:M] = u_ref[:,0:M] -u_ref = u_ref2 -del u_ref2 - -u02 = np.zeros([N,M],dtype='float32') -u02[:,0:M] = u0[:,0:M] -u0 = u02 -del u02 - -Im2 = np.zeros([N,M],dtype='float32') -Im2[:,0:M] = Im[:,0:M] -Im = Im2 -del Im2 -""" + +plt.figure() +plt.imshow(u0, cmap="gray") +plt.show() #%% print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") @@ -180,7 +167,7 @@ def printParametersToString(pars): 'nonneg': 1, 'lipschitz_const' : 8} -print ("#############PD TV CPU####################") +print ("#############PD TV GPU####################") start_time = timeit.default_timer() pd_gpu= PD_TV(pars['input'], pars['regularisation_parameter'], @@ -318,7 +305,7 @@ def printParametersToString(pars): 'LipshitzConstant' :12 ,\ 'tolerance_constant':1e-06} -print ("#############TGV CPU####################") +print ("#############TGV GPU####################") start_time = timeit.default_timer() tgv_gpu = TGV(pars['input'], pars['regularisation_parameter'], @@ -412,7 +399,7 @@ def printParametersToString(pars): 'time_marching_parameter':0.001,\ 'tolerance_constant':1e-06} -print ("#############DIFF4th CPU################") +print ("#############DIFF4th GPU################") start_time = timeit.default_timer() diff4_gpu = Diff4th(pars['input'], pars['regularisation_parameter'], @@ -470,7 +457,7 @@ def printParametersToString(pars): print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") ## plot fig = plt.figure() -plt.suptitle('Performance of NLTV regulariser using the CPU') +plt.suptitle('Performance of NLTV regulariser using the GPU') a=fig.add_subplot(1,2,1) a.set_title('Noisy Image') imgplot = plt.imshow(u0,cmap="gray") diff --git a/demos/demo_gpu_regularisers3D.py b/demos/demo_gpu_regularisers3D.py index 603b175..5e0c759 100644 --- a/demos/demo_gpu_regularisers3D.py +++ b/demos/demo_gpu_regularisers3D.py @@ -12,6 +12,9 @@ import numpy as np import os import timeit +from imageio.v2 import imread + + from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th from ccpi.supp.qualitymetrics import QualityTools ############################################################################### @@ -30,14 +33,12 @@ def printParametersToString(pars): return txt ############################################################################### #%% -os.chdir(os.path.join("..", "demos")) -filename = os.path.join( "data" ,"lena_gray_512.tif") +filename = os.path.join( "../test/test_data" ,"peppers.tif") # read image -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') +Im = imread(filename) -Im = Im/255 +Im = Im/255.0 perc = 0.05 u0 = Im + np.random.normal(loc = 0 , scale = perc * Im , @@ -50,34 +51,9 @@ def printParametersToString(pars): # f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) u0 = u0.astype('float32') u_ref = u_ref.astype('float32') -""" -M = M-100 -u_ref2 = np.zeros([N,M],dtype='float32') -u_ref2[:,0:M] = u_ref[:,0:M] -u_ref = u_ref2 -del u_ref2 - -u02 = np.zeros([N,M],dtype='float32') -u02[:,0:M] = u0[:,0:M] -u0 = u02 -del u02 - -Im2 = np.zeros([N,M],dtype='float32') -Im2[:,0:M] = Im[:,0:M] -Im = Im2 -del Im2 -""" - slices = 20 -filename = os.path.join( "data" ,"lena_gray_512.tif") -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') - -Im = Im/255 -perc = 0.05 - noisyVol = np.zeros((slices,N,N),dtype='float32') noisyRef = np.zeros((slices,N,N),dtype='float32') idealVol = np.zeros((slices,N,N),dtype='float32') @@ -108,7 +84,7 @@ def printParametersToString(pars): 'time_marching_parameter': 0.0007,\ 'tolerance_constant':1e-06} -print ("#############ROF TV CPU####################") +print ("#############ROF TV GPU####################") start_time = timeit.default_timer() rof_gpu3D = ROF_TV(pars['input'], pars['regularisation_parameter'], @@ -285,7 +261,7 @@ def printParametersToString(pars): 'time_marching_parameter' :0.001 ,\ 'tolerance_constant':1e-06} -print ("#############LLT ROF CPU####################") +print ("#############LLT ROF GPU####################") start_time = timeit.default_timer() lltrof_gpu3D = LLT_ROF(pars['input'], pars['regularisation_parameterROF'], @@ -342,6 +318,7 @@ def printParametersToString(pars): pars['LipshitzConstant'], pars['tolerance_constant'], device='gpu', infovector=info_vec_gpu) + Qtools = QualityTools(idealVol, tgv_gpu3D) pars['rmse'] = Qtools.rmse() txtstr = printParametersToString(pars) @@ -425,7 +402,7 @@ def printParametersToString(pars): 'time_marching_parameter':0.001,\ 'tolerance_constant':1e-06} -print ("#############DIFF4th CPU################") +print ("#############DIFF4th GPU################") start_time = timeit.default_timer() diff4_gpu3D = Diff4th(pars['input'], pars['regularisation_parameter'], diff --git a/demos/demo_gpu_regularisers3D_CuPy.py b/demos/demo_gpu_regularisers3D_CuPy.py index 51e00ac..2280672 100644 --- a/demos/demo_gpu_regularisers3D_CuPy.py +++ b/demos/demo_gpu_regularisers3D_CuPy.py @@ -9,6 +9,8 @@ import cupy as cp import os import timeit +from imageio.v2 import imread + from ccpi.filters.regularisersCuPy import ROF_TV as ROF_TV_cupy from ccpi.filters.regularisersCuPy import PD_TV as PD_TV_cupy @@ -28,13 +30,12 @@ def printParametersToString(pars): txt += '\n' return txt ############################################################################### -filename = os.path.join("data" ,"lena_gray_512.tif") +filename = os.path.join( "../test/test_data" ,"peppers.tif") # read image -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') +Im = imread(filename) -Im = Im/255 +Im = Im/255.0 perc = 0.05 u0 = Im + np.random.normal(loc = 0 , scale = perc * Im , @@ -47,19 +48,16 @@ def printParametersToString(pars): u_ref = u_ref.astype('float32') slices = 20 -Im = plt.imread(filename) -Im = np.asarray(Im, dtype='float32') - -Im = Im/255 -perc = 0.05 -noisyVol = np.zeros((slices,N,N),dtype='float32') -idealVol = np.zeros((slices,N,N),dtype='float32') +noisyVol = np.zeros((slices,N,M),dtype='float32') +noisyRef = np.zeros((slices,N,M),dtype='float32') +idealVol = np.zeros((slices,N,M),dtype='float32') for i in range (slices): - noisyVol[i,:,:] = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) + noisyVol[i,:,:] = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) + noisyRef[i,:,:] = Im + np.random.normal(loc = 0 , scale = 0.01 * Im , size = np.shape(Im)) idealVol[i,:,:] = Im - + noisyVol = np.float32(noisyVol) # move numpy array to CuPy. @@ -127,8 +125,7 @@ def printParametersToString(pars): with cp.cuda.Device(gpu_device): pdtv_gpu3D = PD_TV_cupy(noisyVol_cp, regularisation_parameter=0.06, - iterations = 3000, - tolerance_param = 0.0, + iterations = 2000, methodTV=0, nonneg=0, lipschitz_const=8, diff --git a/src/Python/ccpi/filters/regularisersCuPy.py b/src/Python/ccpi/filters/regularisersCuPy.py index 16cf90f..3a1f70e 100644 --- a/src/Python/ccpi/filters/regularisersCuPy.py +++ b/src/Python/ccpi/filters/regularisersCuPy.py @@ -32,14 +32,16 @@ "PD_TV", ] -def ROF_TV(data: cp.ndarray, - regularisation_parameter: Optional[float] = 1e-05, - iterations: Optional[int] = 3000, - time_marching_parameter: Optional[float] = 0.001, - gpu_id: Optional[int] = 0, - ) -> cp.ndarray: - """Total Variation using Rudin-Osher-Fatemi (ROF) explicit iteration scheme to perform edge-preserving image denoising. - This is a gradient-based algorithm for a smoothed TV term which requires a small time marching parameter and a significant number of iterations. + +def ROF_TV( + data: cp.ndarray, + regularisation_parameter: Optional[float] = 1e-05, + iterations: Optional[int] = 3000, + time_marching_parameter: Optional[float] = 0.001, + gpu_id: Optional[int] = 0, +) -> cp.ndarray: + """Total Variation using Rudin-Osher-Fatemi (ROF) explicit iteration scheme to perform edge-preserving image denoising. + This is a gradient-based algorithm for a smoothed TV term which requires a small time marching parameter and a significant number of iterations. Ref: Rudin, Osher, Fatemi, "Nonlinear Total Variation based noise removal algorithms", 1992. Args: @@ -57,7 +59,7 @@ def ROF_TV(data: cp.ndarray, else: raise ValueError("The gpu_device must be a positive integer or zero") cp.get_default_memory_pool().free_all_blocks() - + input_type = data.dtype if input_type != "float32": @@ -67,7 +69,7 @@ def ROF_TV(data: cp.ndarray, out = data.copy() d_D1 = cp.empty(data.shape, dtype=cp.float32, order="C") d_D2 = cp.empty(data.shape, dtype=cp.float32, order="C") - + # loading and compiling CUDA kernels: module = load_cuda_module("TV_ROF_GPU_kernels") if data.ndim == 3: @@ -80,11 +82,11 @@ def ROF_TV(data: cp.ndarray, grid_x = (dx + block_x - 1) // block_x grid_y = dy grid_z = dz - grid_dims = (grid_x, grid_y, grid_z) + grid_dims = (grid_x, grid_y, grid_z) D1_func = module.get_function("D1_func3D") D2_func = module.get_function("D2_func3D") - D3_func = module.get_function("D3_func3D") - TV_kernel = module.get_function("TV_kernel3D") + D3_func = module.get_function("D3_func3D") + TV_kernel = module.get_function("TV_kernel3D") else: data3d = False dy, dx = data.shape @@ -96,11 +98,11 @@ def ROF_TV(data: cp.ndarray, grid_dims = (grid_x, grid_y) D1_func = module.get_function("D1_func2D") D2_func = module.get_function("D2_func2D") - TV_kernel = module.get_function("TV_kernel2D") - + TV_kernel = module.get_function("TV_kernel2D") + # perform algorithm iterations for iter in range(iterations): - # calculate differences + # calculate differences if data3d: params1 = (out, d_D1, dx, dy, dz) else: @@ -119,32 +121,51 @@ def ROF_TV(data: cp.ndarray, cp.cuda.runtime.deviceSynchronize() # calculating the divergence and the gradient term if data3d: - params3 = (d_D1, d_D2, d_D3, out, data, cp.float32(regularisation_parameter), cp.float32(time_marching_parameter), dx, dy, dz) + params3 = ( + d_D1, + d_D2, + d_D3, + out, + data, + cp.float32(regularisation_parameter), + cp.float32(time_marching_parameter), + dx, + dy, + dz, + ) else: - params3 = (d_D1, d_D2, out, data, cp.float32(regularisation_parameter), cp.float32(time_marching_parameter), dx, dy) + params3 = ( + d_D1, + d_D2, + out, + data, + cp.float32(regularisation_parameter), + cp.float32(time_marching_parameter), + dx, + dy, + ) TV_kernel(grid_dims, block_dims, params3) cp.cuda.runtime.deviceSynchronize() return out -def PD_TV(data: cp.ndarray, - regularisation_parameter: Optional[float] = 1e-05, - iterations: Optional[int] = 1000, - tolerance_param: Optional[float] = 0.0, - methodTV: Optional[int] = 0, - nonneg: Optional[int] = 0, - lipschitz_const: Optional[float] = 8.0, - gpu_id: Optional[int] = 0, - ) -> cp.ndarray: +def PD_TV( + data: cp.ndarray, + regularisation_parameter: Optional[float] = 1e-05, + iterations: Optional[int] = 1000, + methodTV: Optional[int] = 0, + nonneg: Optional[int] = 0, + lipschitz_const: Optional[float] = 8.0, + gpu_id: Optional[int] = 0, +) -> cp.ndarray: """Primal Dual algorithm for non-smooth convex Total Variation functional. - Ref: Chambolle, Pock, "A First-Order Primal-Dual Algorithm for Convex Problems + Ref: Chambolle, Pock, "A First-Order Primal-Dual Algorithm for Convex Problems with Applications to Imaging", 2010. Args: data (cp.ndarray): A 2d or 3d CuPy array. regularisation_parameter (Optional[float], optional): Regularisation parameter to control the level of smoothing. Defaults to 1e-05. iterations (Optional[int], optional): The number of iterations. Defaults to 1000. - tolerance_param (Optional[float], optional): If iterations needs to be stopped prematurely based on the error between updates, 0 means no stopping. methodTV (Optional[int], optional): Choose between isotropic (0) or anisotropic (1) case for TV norm. nonneg (Optional[int], optional): Enable non-negativity in updates by selecting 1. Defaults to 0. lipschitz_const (Optional[float], optional): Lipschitz constant to control convergence. @@ -157,27 +178,27 @@ def PD_TV(data: cp.ndarray, cp.cuda.Device(gpu_id).use() else: raise ValueError("The gpu_device must be a positive integer or zero") - - #with cp.cuda.Device(gpu_id): + + # with cp.cuda.Device(gpu_id): cp.get_default_memory_pool().free_all_blocks() - + input_type = data.dtype if input_type != "float32": raise ValueError("The input data should be float32 data type") - + # prepare some parameters: - tau = cp.float32(regularisation_parameter*0.1) - sigma = cp.float32(1.0/(lipschitz_const*tau)) + tau = cp.float32(regularisation_parameter * 0.1) + sigma = cp.float32(1.0 / (lipschitz_const * tau)) theta = cp.float32(1.0) - lt = cp.float32(tau/regularisation_parameter) + lt = cp.float32(tau / regularisation_parameter) # initialise CuPy arrays here: out = data.copy() P1 = cp.empty(data.shape, dtype=cp.float32, order="C") P2 = cp.empty(data.shape, dtype=cp.float32, order="C") d_old = cp.empty(data.shape, dtype=cp.float32, order="C") - + # loading and compiling CUDA kernels: module = load_cuda_module("TV_PD_GPU_kernels") if data.ndim == 3: @@ -212,17 +233,19 @@ def PD_TV(data: cp.ndarray, DivProj_kernel = module.get_function("DivProj2D_kernel") PDnonneg_kernel = module.get_function("PDnonneg2D_kernel") getU_kernel = module.get_function("getU2D_kernel") - + # perform algorithm iterations for iter in range(iterations): # calculate differences if data3d: - params1 = (out, P1, P2, P3, sigma, dx, dy, dz) + params1 = (out, P1, P2, P3, sigma, dx, dy, dz) else: params1 = (out, P1, P2, sigma, dx, dy) - dualPD_kernel(grid_dims, block_dims, params1) # computing the the dual P variable + dualPD_kernel( + grid_dims, block_dims, params1 + ) # computing the the dual P variable cp.cuda.runtime.deviceSynchronize() - if (nonneg != 0): + if nonneg != 0: if data3d: params2 = (out, dx, dy, dz) else: @@ -233,24 +256,24 @@ def PD_TV(data: cp.ndarray, params3 = (P1, P2, P3, dx, dy, dz) else: params3 = (P1, P2, dx, dy) - if (methodTV == 0): + if methodTV == 0: Proj_funcPD_iso_kernel(grid_dims, block_dims, params3) else: - Proj_funcPD_aniso_kernel(grid_dims, block_dims, params3) + Proj_funcPD_aniso_kernel(grid_dims, block_dims, params3) cp.cuda.runtime.deviceSynchronize() d_old = out.copy() - + if data3d: params4 = (out, data, P1, P2, P3, lt, tau, dx, dy, dz) else: params4 = (out, data, P1, P2, lt, tau, dx, dy) - DivProj_kernel(grid_dims, block_dims, params4) # calculate divergence + DivProj_kernel(grid_dims, block_dims, params4) # calculate divergence cp.cuda.runtime.deviceSynchronize() - + if data3d: params5 = (out, d_old, theta, dx, dy, dz) else: params5 = (out, d_old, theta, dx, dy) getU_kernel(grid_dims, block_dims, params5) cp.cuda.runtime.deviceSynchronize() - return out \ No newline at end of file + return out diff --git a/test/test_cupy_regul.py b/test/test_cupy_regul.py index 1783538..b48b43d 100644 --- a/test/test_cupy_regul.py +++ b/test/test_cupy_regul.py @@ -52,7 +52,6 @@ def test_PD_TV_2d(device_data): device_data[60, :, :], regularisation_parameter=0.06, iterations=1000, - tolerance_param=0.0, methodTV=0, nonneg=0, lipschitz_const=8, @@ -74,7 +73,6 @@ def test_PD_TV_3d(device_data): device_data, regularisation_parameter=0.06, iterations=1000, - tolerance_param=0.0, methodTV=0, nonneg=0, lipschitz_const=8, From 7225c202c2f1d460fea60408f8eecf6a0a6493a7 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Fri, 29 Nov 2024 09:27:33 +0000 Subject: [PATCH 13/15] adding pytest_configure hook, remove NDarray import and run linting --- .../SoftwareX_supp/Demo_RealData_Recon_SX.py | 201 ++-- .../Demo_SimulData_ParOptimis_SX.py | 207 ++-- .../SoftwareX_supp/Demo_SimulData_Recon_SX.py | 348 ++++--- demos/SoftwareX_supp/Demo_SimulData_SX.py | 128 ++- demos/SoftwareX_supp/Demo_VolumeDenoise.py | 746 ++++++++------ demos/data/binary_save.py | 9 +- demos/demo_cpu_regularisers.py | 971 ++++++++++-------- demos/demo_cpu_regularisers3D.py | 848 ++++++++------- demos/demo_gpu_regularisers.py | 907 +++++++++------- demos/demo_gpu_regularisers3D.py | 828 ++++++++------- demos/demo_gpu_regularisers3D_CuPy.py | 186 ++-- demos/multi_gpu.py | 84 +- src/Python/ccpi/filters/TV.py | 966 +++++++++++------ src/Python/ccpi/filters/diffusion.py | 258 +++-- src/Python/ccpi/filters/regularisers.py | 30 +- src/Python/ccpi/filters/utils.py | 8 +- src/Python/ccpi/supp/qualitymetrics.py | 35 +- test/conftest.py | 4 + test/test_3d_cpu_vs_gpu.py | 1 - 19 files changed, 4007 insertions(+), 2758 deletions(-) diff --git a/demos/SoftwareX_supp/Demo_RealData_Recon_SX.py b/demos/SoftwareX_supp/Demo_RealData_Recon_SX.py index e60af6e..6797549 100644 --- a/demos/SoftwareX_supp/Demo_RealData_Recon_SX.py +++ b/demos/SoftwareX_supp/Demo_RealData_Recon_SX.py @@ -28,62 +28,66 @@ import time # load dendritic projection data -h5f = h5py.File('data/DendrData_3D.h5','r') -dataRaw = h5f['dataRaw'][:] -flats = h5f['flats'][:] -darks = h5f['darks'][:] -angles_rad = h5f['angles_rad'][:] +h5f = h5py.File("data/DendrData_3D.h5", "r") +dataRaw = h5f["dataRaw"][:] +flats = h5f["flats"][:] +darks = h5f["darks"][:] +angles_rad = h5f["angles_rad"][:] h5f.close() -#%% +# %% # normalise the data [detectorsVert, Projections, detectorsHoriz] -data_norm = normaliser(dataRaw, flats, darks, log='log') +data_norm = normaliser(dataRaw, flats, darks, log="log") del dataRaw, darks, flats intens_max = 2.3 plt.figure() plt.subplot(131) -plt.imshow(data_norm[:,150,:],vmin=0, vmax=intens_max) -plt.title('2D Projection (analytical)') +plt.imshow(data_norm[:, 150, :], vmin=0, vmax=intens_max) +plt.title("2D Projection (analytical)") plt.subplot(132) -plt.imshow(data_norm[300,:,:],vmin=0, vmax=intens_max) -plt.title('Sinogram view') +plt.imshow(data_norm[300, :, :], vmin=0, vmax=intens_max) +plt.title("Sinogram view") plt.subplot(133) -plt.imshow(data_norm[:,:,600],vmin=0, vmax=intens_max) -plt.title('Tangentogram view') +plt.imshow(data_norm[:, :, 600], vmin=0, vmax=intens_max) +plt.title("Tangentogram view") plt.show() -detectorHoriz = np.size(data_norm,2) -det_y_crop = [i for i in range(0,detectorHoriz-22)] -N_size = 950 # reconstruction domain +detectorHoriz = np.size(data_norm, 2) +det_y_crop = [i for i in range(0, detectorHoriz - 22)] +N_size = 950 # reconstruction domain time_label = int(time.time()) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("%%%%%%%%%%%%Reconstructing with FBP method %%%%%%%%%%%%%%%%%") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("%%%%%%%%%%%%Reconstructing with FBP method %%%%%%%%%%%%%%%%%") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") from tomobar.methodsDIR import RecToolsDIR -RectoolsDIR = RecToolsDIR(DetectorsDimH = np.size(det_y_crop), # DetectorsDimH # detector dimension (horizontal) - DetectorsDimV = 100, # DetectorsDimV # detector dimension (vertical) for 3D case only - AnglesVec = angles_rad, # array of angles in radians - ObjSize = N_size, # a scalar to define reconstructed object dimensions - device='gpu') +RectoolsDIR = RecToolsDIR( + DetectorsDimH=np.size( + det_y_crop + ), # DetectorsDimH # detector dimension (horizontal) + DetectorsDimV=100, # DetectorsDimV # detector dimension (vertical) for 3D case only + AnglesVec=angles_rad, # array of angles in radians + ObjSize=N_size, # a scalar to define reconstructed object dimensions + device="gpu", +) -FBPrec = RectoolsDIR.FBP(data_norm[0:100,:,det_y_crop]) +FBPrec = RectoolsDIR.FBP(data_norm[0:100, :, det_y_crop]) sliceSel = 50 max_val = 0.003 plt.figure() plt.subplot(131) -plt.imshow(FBPrec[sliceSel,:,:],vmin=0, vmax=max_val, cmap="gray") -plt.title('FBP Reconstruction, axial view') +plt.imshow(FBPrec[sliceSel, :, :], vmin=0, vmax=max_val, cmap="gray") +plt.title("FBP Reconstruction, axial view") plt.subplot(132) -plt.imshow(FBPrec[:,sliceSel,:],vmin=0, vmax=max_val, cmap="gray") -plt.title('FBP Reconstruction, coronal view') +plt.imshow(FBPrec[:, sliceSel, :], vmin=0, vmax=max_val, cmap="gray") +plt.title("FBP Reconstruction, coronal view") plt.subplot(133) -plt.imshow(FBPrec[:,:,sliceSel],vmin=0, vmax=max_val, cmap="gray") -plt.title('FBP Reconstruction, sagittal view') +plt.imshow(FBPrec[:, :, sliceSel], vmin=0, vmax=max_val, cmap="gray") +plt.title("FBP Reconstruction, sagittal view") plt.show() # saving to tiffs (16bit) @@ -98,44 +102,51 @@ tiff.write_image(np.uint16(FBPrec[i,:,:]*multiplier)) tiff.close() """ -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("Reconstructing with ADMM method using tomobar software") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("Reconstructing with ADMM method using tomobar software") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") # initialise tomobar ITERATIVE reconstruction class ONCE from tomobar.methodsIR import RecToolsIR -RectoolsIR = RecToolsIR(DetectorsDimH = np.size(det_y_crop), # DetectorsDimH # detector dimension (horizontal) - DetectorsDimV = 100, # DetectorsDimV # detector dimension (vertical) for 3D case only - AnglesVec = angles_rad, # array of angles in radians - ObjSize = N_size, # a scalar to define reconstructed object dimensions - datafidelity='LS',# data fidelity, choose LS, PWLS, GH (wip), Students t (wip) - nonnegativity='ENABLE', # enable nonnegativity constraint (set to 'ENABLE') - OS_number = None, # the number of subsets, NONE/(or > 1) ~ classical / ordered subsets - tolerance = 0.0, # tolerance to stop inner (regularisation) iterations earlier - device='gpu') -#%% -print ("Reconstructing with ADMM method using SB-TV penalty") -RecADMM_reg_sbtv = RectoolsIR.ADMM(data_norm[0:100,:,det_y_crop], - rho_const = 2000.0, \ - iterationsADMM = 15, \ - regularisation = 'SB_TV', \ - regularisation_parameter = 0.00085,\ - regularisation_iterations = 50) + +RectoolsIR = RecToolsIR( + DetectorsDimH=np.size( + det_y_crop + ), # DetectorsDimH # detector dimension (horizontal) + DetectorsDimV=100, # DetectorsDimV # detector dimension (vertical) for 3D case only + AnglesVec=angles_rad, # array of angles in radians + ObjSize=N_size, # a scalar to define reconstructed object dimensions + datafidelity="LS", # data fidelity, choose LS, PWLS, GH (wip), Students t (wip) + nonnegativity="ENABLE", # enable nonnegativity constraint (set to 'ENABLE') + OS_number=None, # the number of subsets, NONE/(or > 1) ~ classical / ordered subsets + tolerance=0.0, # tolerance to stop inner (regularisation) iterations earlier + device="gpu", +) +# %% +print("Reconstructing with ADMM method using SB-TV penalty") +RecADMM_reg_sbtv = RectoolsIR.ADMM( + data_norm[0:100, :, det_y_crop], + rho_const=2000.0, + iterationsADMM=15, + regularisation="SB_TV", + regularisation_parameter=0.00085, + regularisation_iterations=50, +) sliceSel = 50 max_val = 0.003 plt.figure() plt.subplot(131) -plt.imshow(RecADMM_reg_sbtv[sliceSel,:,:],vmin=0, vmax=max_val, cmap="gray") -plt.title('3D ADMM-SB-TV Reconstruction, axial view') +plt.imshow(RecADMM_reg_sbtv[sliceSel, :, :], vmin=0, vmax=max_val, cmap="gray") +plt.title("3D ADMM-SB-TV Reconstruction, axial view") plt.subplot(132) -plt.imshow(RecADMM_reg_sbtv[:,sliceSel,:],vmin=0, vmax=max_val, cmap="gray") -plt.title('3D ADMM-SB-TV Reconstruction, coronal view') +plt.imshow(RecADMM_reg_sbtv[:, sliceSel, :], vmin=0, vmax=max_val, cmap="gray") +plt.title("3D ADMM-SB-TV Reconstruction, coronal view") plt.subplot(133) -plt.imshow(RecADMM_reg_sbtv[:,:,sliceSel],vmin=0, vmax=max_val, cmap="gray") -plt.title('3D ADMM-SB-TV Reconstruction, sagittal view') +plt.imshow(RecADMM_reg_sbtv[:, :, sliceSel], vmin=0, vmax=max_val, cmap="gray") +plt.title("3D ADMM-SB-TV Reconstruction, sagittal view") plt.show() @@ -149,33 +160,35 @@ tiff.close() """ # Saving recpnstructed data with a unique time label -np.save('Dendr_ADMM_SBTV'+str(time_label)+'.npy', RecADMM_reg_sbtv) +np.save("Dendr_ADMM_SBTV" + str(time_label) + ".npy", RecADMM_reg_sbtv) del RecADMM_reg_sbtv -#%% -print ("Reconstructing with ADMM method using ROF-LLT penalty") -RecADMM_reg_rofllt = RectoolsIR.ADMM(data_norm[0:100,:,det_y_crop], - rho_const = 2000.0, \ - iterationsADMM = 15, \ - regularisation = 'LLT_ROF', \ - regularisation_parameter = 0.0009,\ - regularisation_parameter2 = 0.0007,\ - time_marching_parameter = 0.001,\ - regularisation_iterations = 550) +# %% +print("Reconstructing with ADMM method using ROF-LLT penalty") +RecADMM_reg_rofllt = RectoolsIR.ADMM( + data_norm[0:100, :, det_y_crop], + rho_const=2000.0, + iterationsADMM=15, + regularisation="LLT_ROF", + regularisation_parameter=0.0009, + regularisation_parameter2=0.0007, + time_marching_parameter=0.001, + regularisation_iterations=550, +) sliceSel = 50 max_val = 0.003 plt.figure() plt.subplot(131) -plt.imshow(RecADMM_reg_rofllt[sliceSel,:,:],vmin=0, vmax=max_val) -plt.title('3D ADMM-ROFLLT Reconstruction, axial view') +plt.imshow(RecADMM_reg_rofllt[sliceSel, :, :], vmin=0, vmax=max_val) +plt.title("3D ADMM-ROFLLT Reconstruction, axial view") plt.subplot(132) -plt.imshow(RecADMM_reg_rofllt[:,sliceSel,:],vmin=0, vmax=max_val) -plt.title('3D ADMM-ROFLLT Reconstruction, coronal view') +plt.imshow(RecADMM_reg_rofllt[:, sliceSel, :], vmin=0, vmax=max_val) +plt.title("3D ADMM-ROFLLT Reconstruction, coronal view") plt.subplot(133) -plt.imshow(RecADMM_reg_rofllt[:,:,sliceSel],vmin=0, vmax=max_val) -plt.title('3D ADMM-ROFLLT Reconstruction, sagittal view') +plt.imshow(RecADMM_reg_rofllt[:, :, sliceSel], vmin=0, vmax=max_val) +plt.title("3D ADMM-ROFLLT Reconstruction, sagittal view") plt.show() # saving to tiffs (16bit) @@ -189,31 +202,33 @@ """ # Saving recpnstructed data with a unique time label -np.save('Dendr_ADMM_ROFLLT'+str(time_label)+'.npy', RecADMM_reg_rofllt) +np.save("Dendr_ADMM_ROFLLT" + str(time_label) + ".npy", RecADMM_reg_rofllt) del RecADMM_reg_rofllt -#%% -print ("Reconstructing with ADMM method using TGV penalty") -RecADMM_reg_tgv = RectoolsIR.ADMM(data_norm[0:100,:,det_y_crop], - rho_const = 2000.0, \ - iterationsADMM = 15, \ - regularisation = 'TGV', \ - regularisation_parameter = 0.01,\ - regularisation_iterations = 500) +# %% +print("Reconstructing with ADMM method using TGV penalty") +RecADMM_reg_tgv = RectoolsIR.ADMM( + data_norm[0:100, :, det_y_crop], + rho_const=2000.0, + iterationsADMM=15, + regularisation="TGV", + regularisation_parameter=0.01, + regularisation_iterations=500, +) sliceSel = 50 max_val = 0.003 plt.figure() plt.subplot(131) -plt.imshow(RecADMM_reg_tgv[sliceSel,:,:],vmin=0, vmax=max_val) -plt.title('3D ADMM-TGV Reconstruction, axial view') +plt.imshow(RecADMM_reg_tgv[sliceSel, :, :], vmin=0, vmax=max_val) +plt.title("3D ADMM-TGV Reconstruction, axial view") plt.subplot(132) -plt.imshow(RecADMM_reg_tgv[:,sliceSel,:],vmin=0, vmax=max_val) -plt.title('3D ADMM-TGV Reconstruction, coronal view') +plt.imshow(RecADMM_reg_tgv[:, sliceSel, :], vmin=0, vmax=max_val) +plt.title("3D ADMM-TGV Reconstruction, coronal view") plt.subplot(133) -plt.imshow(RecADMM_reg_tgv[:,:,sliceSel],vmin=0, vmax=max_val) -plt.title('3D ADMM-TGV Reconstruction, sagittal view') +plt.imshow(RecADMM_reg_tgv[:, :, sliceSel], vmin=0, vmax=max_val) +plt.title("3D ADMM-TGV Reconstruction, sagittal view") plt.show() # saving to tiffs (16bit) @@ -226,6 +241,6 @@ tiff.close() """ # Saving recpnstructed data with a unique time label -np.save('Dendr_ADMM_TGV'+str(time_label)+'.npy', RecADMM_reg_tgv) +np.save("Dendr_ADMM_TGV" + str(time_label) + ".npy", RecADMM_reg_tgv) del RecADMM_reg_tgv -#%% +# %% diff --git a/demos/SoftwareX_supp/Demo_SimulData_ParOptimis_SX.py b/demos/SoftwareX_supp/Demo_SimulData_ParOptimis_SX.py index 0925cf6..9b52076 100644 --- a/demos/SoftwareX_supp/Demo_SimulData_ParOptimis_SX.py +++ b/demos/SoftwareX_supp/Demo_SimulData_ParOptimis_SX.py @@ -19,142 +19,169 @@ @author: Daniil Kazantsev, e:mail daniil.kazantsev@diamond.ac.uk GPLv3 license (ASTRA toolbox) """ -#import timeit +# import timeit import matplotlib.pyplot as plt import numpy as np import h5py from ccpi.supp.qualitymetrics import QualityTools -# loading the data -h5f = h5py.File('data/TomoSim_data1550671417.h5','r') -phantom = h5f['phantom'][:] -projdata_norm = h5f['projdata_norm'][:] -proj_angles = h5f['proj_angles'][:] +# loading the data +h5f = h5py.File("data/TomoSim_data1550671417.h5", "r") +phantom = h5f["phantom"][:] +projdata_norm = h5f["projdata_norm"][:] +proj_angles = h5f["proj_angles"][:] h5f.close() [Vert_det, AnglesNum, Horiz_det] = np.shape(projdata_norm) N_size = Vert_det sliceSel = 128 -#plt.gray() -plt.figure() +# plt.gray() +plt.figure() plt.subplot(131) -plt.imshow(phantom[sliceSel,:,:],vmin=0, vmax=1) -plt.title('3D Phantom, axial view') +plt.imshow(phantom[sliceSel, :, :], vmin=0, vmax=1) +plt.title("3D Phantom, axial view") plt.subplot(132) -plt.imshow(phantom[:,sliceSel,:],vmin=0, vmax=1) -plt.title('3D Phantom, coronal view') +plt.imshow(phantom[:, sliceSel, :], vmin=0, vmax=1) +plt.title("3D Phantom, coronal view") plt.subplot(133) -plt.imshow(phantom[:,:,sliceSel],vmin=0, vmax=1) -plt.title('3D Phantom, sagittal view') +plt.imshow(phantom[:, :, sliceSel], vmin=0, vmax=1) +plt.title("3D Phantom, sagittal view") plt.show() intens_max = 240 -plt.figure() +plt.figure() plt.subplot(131) -plt.imshow(projdata_norm[:,sliceSel,:],vmin=0, vmax=intens_max) -plt.title('2D Projection (erroneous)') +plt.imshow(projdata_norm[:, sliceSel, :], vmin=0, vmax=intens_max) +plt.title("2D Projection (erroneous)") plt.subplot(132) -plt.imshow(projdata_norm[sliceSel,:,:],vmin=0, vmax=intens_max) -plt.title('Sinogram view') +plt.imshow(projdata_norm[sliceSel, :, :], vmin=0, vmax=intens_max) +plt.title("Sinogram view") plt.subplot(133) -plt.imshow(projdata_norm[:,:,sliceSel],vmin=0, vmax=intens_max) -plt.title('Tangentogram view') +plt.imshow(projdata_norm[:, :, sliceSel], vmin=0, vmax=intens_max) +plt.title("Tangentogram view") plt.show() -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("Reconstructing with ADMM method using tomobar software") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("Reconstructing with ADMM method using tomobar software") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") # initialise tomobar ITERATIVE reconstruction class ONCE from tomobar.methodsIR import RecToolsIR -RectoolsIR = RecToolsIR(DetectorsDimH = Horiz_det, # DetectorsDimH # detector dimension (horizontal) - DetectorsDimV = Vert_det, # DetectorsDimV # detector dimension (vertical) for 3D case only - AnglesVec = proj_angles, # array of angles in radians - ObjSize = N_size, # a scalar to define reconstructed object dimensions - datafidelity='LS',# data fidelity, choose LS, PWLS (wip), GH (wip), Student (wip) - nonnegativity='ENABLE', # enable nonnegativity constraint (set to 'ENABLE') - OS_number = None, # the number of subsets, NONE/(or > 1) ~ classical / ordered subsets - tolerance = 0.0, # tolerance to stop inner (regularisation) iterations earlier - device='gpu') -#%% + +RectoolsIR = RecToolsIR( + DetectorsDimH=Horiz_det, # DetectorsDimH # detector dimension (horizontal) + DetectorsDimV=Vert_det, # DetectorsDimV # detector dimension (vertical) for 3D case only + AnglesVec=proj_angles, # array of angles in radians + ObjSize=N_size, # a scalar to define reconstructed object dimensions + datafidelity="LS", # data fidelity, choose LS, PWLS (wip), GH (wip), Student (wip) + nonnegativity="ENABLE", # enable nonnegativity constraint (set to 'ENABLE') + OS_number=None, # the number of subsets, NONE/(or > 1) ~ classical / ordered subsets + tolerance=0.0, # tolerance to stop inner (regularisation) iterations earlier + device="gpu", +) +# %% param_space = 30 -reg_param_sb_vec = np.linspace(0.03,0.15,param_space,dtype='float32') # a vector of parameters -erros_vec_sbtv = np.zeros((param_space)) # a vector of errors - -print ("Reconstructing with ADMM method using SB-TV penalty") -for i in range(0,param_space): - RecADMM_reg_sbtv = RectoolsIR.ADMM(projdata_norm, - rho_const = 2000.0, \ - iterationsADMM = 15, \ - regularisation = 'SB_TV', \ - regularisation_parameter = reg_param_sb_vec[i],\ - regularisation_iterations = 50) - # calculate errors +reg_param_sb_vec = np.linspace( + 0.03, 0.15, param_space, dtype="float32" +) # a vector of parameters +erros_vec_sbtv = np.zeros((param_space)) # a vector of errors + +print("Reconstructing with ADMM method using SB-TV penalty") +for i in range(0, param_space): + RecADMM_reg_sbtv = RectoolsIR.ADMM( + projdata_norm, + rho_const=2000.0, + iterationsADMM=15, + regularisation="SB_TV", + regularisation_parameter=reg_param_sb_vec[i], + regularisation_iterations=50, + ) + # calculate errors Qtools = QualityTools(phantom, RecADMM_reg_sbtv) erros_vec_sbtv[i] = Qtools.rmse() - print("RMSE for regularisation parameter {} for ADMM-SB-TV is {}".format(reg_param_sb_vec[i],erros_vec_sbtv[i])) + print( + "RMSE for regularisation parameter {} for ADMM-SB-TV is {}".format( + reg_param_sb_vec[i], erros_vec_sbtv[i] + ) + ) -plt.figure() +plt.figure() plt.plot(erros_vec_sbtv) # Saving generated data with a unique time label -h5f = h5py.File('Optim_admm_sbtv.h5', 'w') -h5f.create_dataset('reg_param_sb_vec', data=reg_param_sb_vec) -h5f.create_dataset('erros_vec_sbtv', data=erros_vec_sbtv) +h5f = h5py.File("Optim_admm_sbtv.h5", "w") +h5f.create_dataset("reg_param_sb_vec", data=reg_param_sb_vec) +h5f.create_dataset("erros_vec_sbtv", data=erros_vec_sbtv) h5f.close() -#%% +# %% param_space = 30 -reg_param_rofllt_vec = np.linspace(0.03,0.15,param_space,dtype='float32') # a vector of parameters -erros_vec_rofllt = np.zeros((param_space)) # a vector of errors - -print ("Reconstructing with ADMM method using ROF-LLT penalty") -for i in range(0,param_space): - RecADMM_reg_rofllt = RectoolsIR.ADMM(projdata_norm, - rho_const = 2000.0, \ - iterationsADMM = 15, \ - regularisation = 'LLT_ROF', \ - regularisation_parameter = reg_param_rofllt_vec[i],\ - regularisation_parameter2 = 0.005,\ - regularisation_iterations = 600) - # calculate errors +reg_param_rofllt_vec = np.linspace( + 0.03, 0.15, param_space, dtype="float32" +) # a vector of parameters +erros_vec_rofllt = np.zeros((param_space)) # a vector of errors + +print("Reconstructing with ADMM method using ROF-LLT penalty") +for i in range(0, param_space): + RecADMM_reg_rofllt = RectoolsIR.ADMM( + projdata_norm, + rho_const=2000.0, + iterationsADMM=15, + regularisation="LLT_ROF", + regularisation_parameter=reg_param_rofllt_vec[i], + regularisation_parameter2=0.005, + regularisation_iterations=600, + ) + # calculate errors Qtools = QualityTools(phantom, RecADMM_reg_rofllt) erros_vec_rofllt[i] = Qtools.rmse() - print("RMSE for regularisation parameter {} for ADMM-ROF-LLT is {}".format(reg_param_rofllt_vec[i],erros_vec_rofllt[i])) + print( + "RMSE for regularisation parameter {} for ADMM-ROF-LLT is {}".format( + reg_param_rofllt_vec[i], erros_vec_rofllt[i] + ) + ) -plt.figure() +plt.figure() plt.plot(erros_vec_rofllt) # Saving generated data with a unique time label -h5f = h5py.File('Optim_admm_rofllt.h5', 'w') -h5f.create_dataset('reg_param_rofllt_vec', data=reg_param_rofllt_vec) -h5f.create_dataset('erros_vec_rofllt', data=erros_vec_rofllt) +h5f = h5py.File("Optim_admm_rofllt.h5", "w") +h5f.create_dataset("reg_param_rofllt_vec", data=reg_param_rofllt_vec) +h5f.create_dataset("erros_vec_rofllt", data=erros_vec_rofllt) h5f.close() -#%% +# %% param_space = 30 -reg_param_tgv_vec = np.linspace(0.03,0.15,param_space,dtype='float32') # a vector of parameters -erros_vec_tgv = np.zeros((param_space)) # a vector of errors - -print ("Reconstructing with ADMM method using TGV penalty") -for i in range(0,param_space): - RecADMM_reg_tgv = RectoolsIR.ADMM(projdata_norm, - rho_const = 2000.0, \ - iterationsADMM = 15, \ - regularisation = 'TGV', \ - regularisation_parameter = reg_param_tgv_vec[i],\ - regularisation_iterations = 600) - # calculate errors +reg_param_tgv_vec = np.linspace( + 0.03, 0.15, param_space, dtype="float32" +) # a vector of parameters +erros_vec_tgv = np.zeros((param_space)) # a vector of errors + +print("Reconstructing with ADMM method using TGV penalty") +for i in range(0, param_space): + RecADMM_reg_tgv = RectoolsIR.ADMM( + projdata_norm, + rho_const=2000.0, + iterationsADMM=15, + regularisation="TGV", + regularisation_parameter=reg_param_tgv_vec[i], + regularisation_iterations=600, + ) + # calculate errors Qtools = QualityTools(phantom, RecADMM_reg_tgv) erros_vec_tgv[i] = Qtools.rmse() - print("RMSE for regularisation parameter {} for ADMM-TGV is {}".format(reg_param_tgv_vec[i],erros_vec_tgv[i])) + print( + "RMSE for regularisation parameter {} for ADMM-TGV is {}".format( + reg_param_tgv_vec[i], erros_vec_tgv[i] + ) + ) -plt.figure() +plt.figure() plt.plot(erros_vec_tgv) # Saving generated data with a unique time label -h5f = h5py.File('Optim_admm_tgv.h5', 'w') -h5f.create_dataset('reg_param_tgv_vec', data=reg_param_tgv_vec) -h5f.create_dataset('erros_vec_tgv', data=erros_vec_tgv) +h5f = h5py.File("Optim_admm_tgv.h5", "w") +h5f.create_dataset("reg_param_tgv_vec", data=reg_param_tgv_vec) +h5f.create_dataset("erros_vec_tgv", data=erros_vec_tgv) h5f.close() -#%% +# %% diff --git a/demos/SoftwareX_supp/Demo_SimulData_Recon_SX.py b/demos/SoftwareX_supp/Demo_SimulData_Recon_SX.py index 1d3dc03..9790863 100644 --- a/demos/SoftwareX_supp/Demo_SimulData_Recon_SX.py +++ b/demos/SoftwareX_supp/Demo_SimulData_Recon_SX.py @@ -18,7 +18,7 @@ @author: Daniil Kazantsev, e:mail daniil.kazantsev@diamond.ac.uk GPLv3 license (ASTRA toolbox) """ -#import timeit +# import timeit import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec import numpy as np @@ -26,283 +26,299 @@ from ccpi.supp.qualitymetrics import QualityTools from scipy.signal import gaussian -# loading the data -h5f = h5py.File('data/TomoSim_data1550671417.h5','r') -phantom = h5f['phantom'][:] -projdata_norm = h5f['projdata_norm'][:] -proj_angles = h5f['proj_angles'][:] +# loading the data +h5f = h5py.File("data/TomoSim_data1550671417.h5", "r") +phantom = h5f["phantom"][:] +projdata_norm = h5f["projdata_norm"][:] +proj_angles = h5f["proj_angles"][:] h5f.close() [Vert_det, AnglesNum, Horiz_det] = np.shape(projdata_norm) N_size = Vert_det # loading optmisation parameters (the result of running Demo_SimulData_ParOptimis_SX) -h5f = h5py.File('optim_param/Optim_admm_sbtv.h5','r') -reg_param_sb_vec = h5f['reg_param_sb_vec'][:] -erros_vec_sbtv = h5f['erros_vec_sbtv'][:] +h5f = h5py.File("optim_param/Optim_admm_sbtv.h5", "r") +reg_param_sb_vec = h5f["reg_param_sb_vec"][:] +erros_vec_sbtv = h5f["erros_vec_sbtv"][:] h5f.close() -h5f = h5py.File('optim_param/Optim_admm_rofllt.h5','r') -reg_param_rofllt_vec = h5f['reg_param_rofllt_vec'][:] -erros_vec_rofllt = h5f['erros_vec_rofllt'][:] +h5f = h5py.File("optim_param/Optim_admm_rofllt.h5", "r") +reg_param_rofllt_vec = h5f["reg_param_rofllt_vec"][:] +erros_vec_rofllt = h5f["erros_vec_rofllt"][:] h5f.close() -h5f = h5py.File('optim_param/Optim_admm_tgv.h5','r') -reg_param_tgv_vec = h5f['reg_param_tgv_vec'][:] -erros_vec_tgv = h5f['erros_vec_tgv'][:] +h5f = h5py.File("optim_param/Optim_admm_tgv.h5", "r") +reg_param_tgv_vec = h5f["reg_param_tgv_vec"][:] +erros_vec_tgv = h5f["erros_vec_tgv"][:] h5f.close() index_minSBTV = np.argmin(erros_vec_sbtv) index_minROFLLT = np.argmin(erros_vec_rofllt) -index_minTGV = np.argmin(erros_vec_tgv) +index_minTGV = np.argmin(erros_vec_tgv) # assign optimal regularisation parameters: optimReg_sbtv = reg_param_sb_vec[index_minSBTV] optimReg_rofllt = reg_param_rofllt_vec[index_minROFLLT] optimReg_tgv = reg_param_tgv_vec[index_minTGV] -#%% +# %% # plot loaded data sliceSel = 128 -#plt.figure() +# plt.figure() fig, (ax1, ax2) = plt.subplots(figsize=(15, 5), ncols=2) -plt.rcParams.update({'xtick.labelsize': 'x-small'}) -plt.rcParams.update({'ytick.labelsize':'x-small'}) +plt.rcParams.update({"xtick.labelsize": "x-small"}) +plt.rcParams.update({"ytick.labelsize": "x-small"}) plt.subplot(121) -one = plt.imshow(phantom[sliceSel,:,:],vmin=0, vmax=1, interpolation='none', cmap="PuOr") +one = plt.imshow( + phantom[sliceSel, :, :], vmin=0, vmax=1, interpolation="none", cmap="PuOr" +) fig.colorbar(one, ax=ax1) -plt.title('3D Phantom, axial (X-Y) view') +plt.title("3D Phantom, axial (X-Y) view") plt.subplot(122) -two = plt.imshow(phantom[:,sliceSel,:],vmin=0, vmax=1,interpolation='none', cmap="PuOr") +two = plt.imshow( + phantom[:, sliceSel, :], vmin=0, vmax=1, interpolation="none", cmap="PuOr" +) fig.colorbar(two, ax=ax2) -plt.title('3D Phantom, coronal (Y-Z) view') +plt.title("3D Phantom, coronal (Y-Z) view") """ plt.subplot(133) plt.imshow(phantom[:,:,sliceSel],vmin=0, vmax=1, cmap="PuOr") plt.title('3D Phantom, sagittal view') """ plt.show() -#%% +# %% intens_max = 220 -plt.figure() -plt.rcParams.update({'xtick.labelsize': 'x-small'}) -plt.rcParams.update({'ytick.labelsize':'x-small'}) +plt.figure() +plt.rcParams.update({"xtick.labelsize": "x-small"}) +plt.rcParams.update({"ytick.labelsize": "x-small"}) plt.subplot(131) -plt.imshow(projdata_norm[:,sliceSel,:],vmin=0, vmax=intens_max, cmap="PuOr") -plt.xlabel('X-detector', fontsize=16) -plt.ylabel('Z-detector', fontsize=16) -plt.title('2D Projection (X-Z) view', fontsize=19) +plt.imshow(projdata_norm[:, sliceSel, :], vmin=0, vmax=intens_max, cmap="PuOr") +plt.xlabel("X-detector", fontsize=16) +plt.ylabel("Z-detector", fontsize=16) +plt.title("2D Projection (X-Z) view", fontsize=19) plt.subplot(132) -plt.imshow(projdata_norm[sliceSel,:,:],vmin=0, vmax=intens_max, cmap="PuOr") -plt.xlabel('X-detector', fontsize=16) -plt.ylabel('Projection angle', fontsize=16) -plt.title('Sinogram (X-Y) view', fontsize=19) +plt.imshow(projdata_norm[sliceSel, :, :], vmin=0, vmax=intens_max, cmap="PuOr") +plt.xlabel("X-detector", fontsize=16) +plt.ylabel("Projection angle", fontsize=16) +plt.title("Sinogram (X-Y) view", fontsize=19) plt.subplot(133) -plt.imshow(projdata_norm[:,:,sliceSel],vmin=0, vmax=intens_max, cmap="PuOr") -plt.xlabel('Projection angle', fontsize=16) -plt.ylabel('Z-detector', fontsize=16) -plt.title('Vertical (Y-Z) view', fontsize=19) +plt.imshow(projdata_norm[:, :, sliceSel], vmin=0, vmax=intens_max, cmap="PuOr") +plt.xlabel("Projection angle", fontsize=16) +plt.ylabel("Z-detector", fontsize=16) +plt.title("Vertical (Y-Z) view", fontsize=19) plt.show() -#plt.savefig('projdata.pdf', format='pdf', dpi=1200) -#%% +# plt.savefig('projdata.pdf', format='pdf', dpi=1200) +# %% # initialise tomobar DIRECT reconstruction class ONCE from tomobar.methodsDIR import RecToolsDIR -RectoolsDIR = RecToolsDIR(DetectorsDimH = Horiz_det, # DetectorsDimH # detector dimension (horizontal) - DetectorsDimV = Vert_det, # DetectorsDimV # detector dimension (vertical) for 3D case only - AnglesVec = proj_angles, # array of angles in radians - ObjSize = N_size, # a scalar to define reconstructed object dimensions - device = 'gpu') -#%% -print ("Reconstruction using FBP from tomobar") -recFBP= RectoolsDIR.FBP(projdata_norm) # FBP reconstruction -#%% -x0, y0 = 0, 127 # These are in _pixel_ coordinates!! + +RectoolsDIR = RecToolsDIR( + DetectorsDimH=Horiz_det, # DetectorsDimH # detector dimension (horizontal) + DetectorsDimV=Vert_det, # DetectorsDimV # detector dimension (vertical) for 3D case only + AnglesVec=proj_angles, # array of angles in radians + ObjSize=N_size, # a scalar to define reconstructed object dimensions + device="gpu", +) +# %% +print("Reconstruction using FBP from tomobar") +recFBP = RectoolsDIR.FBP(projdata_norm) # FBP reconstruction +# %% +x0, y0 = 0, 127 # These are in _pixel_ coordinates!! x1, y1 = 255, 127 -sliceSel = int(0.5*N_size) +sliceSel = int(0.5 * N_size) max_val = 1 -plt.figure(figsize = (20,5)) +plt.figure(figsize=(20, 5)) gs1 = gridspec.GridSpec(1, 3) -gs1.update(wspace=0.1, hspace=0.05) # set the spacing between axes. +gs1.update(wspace=0.1, hspace=0.05) # set the spacing between axes. ax1 = plt.subplot(gs1[0]) -plt.imshow(recFBP[sliceSel,:,:],vmin=0, vmax=max_val, cmap="PuOr") -ax1.plot([x0, x1], [y0, y1], 'ko-', linestyle='--') +plt.imshow(recFBP[sliceSel, :, :], vmin=0, vmax=max_val, cmap="PuOr") +ax1.plot([x0, x1], [y0, y1], "ko-", linestyle="--") plt.colorbar(ax=ax1) -plt.title('FBP Reconstruction, axial (X-Y) view', fontsize=19) -ax1.set_aspect('equal') +plt.title("FBP Reconstruction, axial (X-Y) view", fontsize=19) +ax1.set_aspect("equal") ax3 = plt.subplot(gs1[1]) -plt.plot(phantom[sliceSel,sliceSel,0:N_size],color='k',linewidth=2) -plt.plot(recFBP[sliceSel,sliceSel,0:N_size],linestyle='--',color='g') -plt.title('Profile', fontsize=19) +plt.plot(phantom[sliceSel, sliceSel, 0:N_size], color="k", linewidth=2) +plt.plot(recFBP[sliceSel, sliceSel, 0:N_size], linestyle="--", color="g") +plt.title("Profile", fontsize=19) ax2 = plt.subplot(gs1[2]) -plt.imshow(recFBP[:,sliceSel,:],vmin=0, vmax=max_val, cmap="PuOr") -plt.title('FBP Reconstruction, coronal (Y-Z) view', fontsize=19) -ax2.set_aspect('equal') +plt.imshow(recFBP[:, sliceSel, :], vmin=0, vmax=max_val, cmap="PuOr") +plt.title("FBP Reconstruction, coronal (Y-Z) view", fontsize=19) +ax2.set_aspect("equal") plt.show() -#plt.savefig('FBP_phantom.pdf', format='pdf', dpi=1600) +# plt.savefig('FBP_phantom.pdf', format='pdf', dpi=1600) -# calculate errors +# calculate errors Qtools = QualityTools(phantom, recFBP) RMSE_fbp = Qtools.rmse() print("Root Mean Square Error for FBP is {}".format(RMSE_fbp)) # SSIM measure -Qtools = QualityTools(phantom[128,:,:]*255, recFBP[128,:,:]*235) +Qtools = QualityTools(phantom[128, :, :] * 255, recFBP[128, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_fbp = Qtools.ssim(win2d) print("Mean SSIM for FBP is {}".format(ssim_fbp[0])) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("Reconstructing with ADMM method using tomobar software") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("Reconstructing with ADMM method using tomobar software") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") # initialise tomobar ITERATIVE reconstruction class ONCE from tomobar.methodsIR import RecToolsIR -RectoolsIR = RecToolsIR(DetectorsDimH = Horiz_det, # DetectorsDimH # detector dimension (horizontal) - DetectorsDimV = Vert_det, # DetectorsDimV # detector dimension (vertical) for 3D case only - AnglesVec = proj_angles, # array of angles in radians - ObjSize = N_size, # a scalar to define reconstructed object dimensions - datafidelity='LS',# data fidelity, choose LS, PWLS (wip), GH (wip), Student (wip) - nonnegativity='ENABLE', # enable nonnegativity constraint (set to 'ENABLE') - OS_number = None, # the number of subsets, NONE/(or > 1) ~ classical / ordered subsets - tolerance = 1e-06, # tolerance to stop outer -ADMM iterations earlier - device='gpu') -#%% -print ("Reconstructing with ADMM method using SB-TV penalty") -RecADMM_reg_sbtv = RectoolsIR.ADMM(projdata_norm, - rho_const = 2000.0, \ - iterationsADMM = 25, \ - regularisation = 'SB_TV', \ - regularisation_parameter = optimReg_sbtv,\ - regularisation_iterations = 50) -sliceSel = int(0.5*N_size) +RectoolsIR = RecToolsIR( + DetectorsDimH=Horiz_det, # DetectorsDimH # detector dimension (horizontal) + DetectorsDimV=Vert_det, # DetectorsDimV # detector dimension (vertical) for 3D case only + AnglesVec=proj_angles, # array of angles in radians + ObjSize=N_size, # a scalar to define reconstructed object dimensions + datafidelity="LS", # data fidelity, choose LS, PWLS (wip), GH (wip), Student (wip) + nonnegativity="ENABLE", # enable nonnegativity constraint (set to 'ENABLE') + OS_number=None, # the number of subsets, NONE/(or > 1) ~ classical / ordered subsets + tolerance=1e-06, # tolerance to stop outer -ADMM iterations earlier + device="gpu", +) +# %% +print("Reconstructing with ADMM method using SB-TV penalty") +RecADMM_reg_sbtv = RectoolsIR.ADMM( + projdata_norm, + rho_const=2000.0, + iterationsADMM=25, + regularisation="SB_TV", + regularisation_parameter=optimReg_sbtv, + regularisation_iterations=50, +) + +sliceSel = int(0.5 * N_size) max_val = 1 -plt.figure(figsize = (20,3)) +plt.figure(figsize=(20, 3)) gs1 = gridspec.GridSpec(1, 4) -gs1.update(wspace=0.02, hspace=0.01) # set the spacing between axes. +gs1.update(wspace=0.02, hspace=0.01) # set the spacing between axes. ax1 = plt.subplot(gs1[0]) -plt.plot(reg_param_sb_vec, erros_vec_sbtv, color='k',linewidth=2) -plt.xlabel('Regularisation parameter', fontsize=16) -plt.ylabel('RMSE value', fontsize=16) -plt.title('Regularisation selection', fontsize=19) +plt.plot(reg_param_sb_vec, erros_vec_sbtv, color="k", linewidth=2) +plt.xlabel("Regularisation parameter", fontsize=16) +plt.ylabel("RMSE value", fontsize=16) +plt.title("Regularisation selection", fontsize=19) ax2 = plt.subplot(gs1[1]) -plt.imshow(RecADMM_reg_sbtv[sliceSel,:,:],vmin=0, vmax=max_val, cmap="PuOr") -ax2.plot([x0, x1], [y0, y1], 'ko-', linestyle='--') -plt.title('ADMM-SBTV (X-Y) view', fontsize=19) -#ax2.set_aspect('equal') +plt.imshow(RecADMM_reg_sbtv[sliceSel, :, :], vmin=0, vmax=max_val, cmap="PuOr") +ax2.plot([x0, x1], [y0, y1], "ko-", linestyle="--") +plt.title("ADMM-SBTV (X-Y) view", fontsize=19) +# ax2.set_aspect('equal') ax3 = plt.subplot(gs1[2]) -plt.plot(phantom[sliceSel,sliceSel,0:N_size],color='k',linewidth=2) -plt.plot(RecADMM_reg_sbtv[sliceSel,sliceSel,0:N_size],linestyle='--',color='g') -plt.title('Profile', fontsize=19) +plt.plot(phantom[sliceSel, sliceSel, 0:N_size], color="k", linewidth=2) +plt.plot(RecADMM_reg_sbtv[sliceSel, sliceSel, 0:N_size], linestyle="--", color="g") +plt.title("Profile", fontsize=19) ax4 = plt.subplot(gs1[3]) -plt.imshow(RecADMM_reg_sbtv[:,sliceSel,:],vmin=0, vmax=max_val, cmap="PuOr") -plt.title('ADMM-SBTV (Y-Z) view', fontsize=19) +plt.imshow(RecADMM_reg_sbtv[:, sliceSel, :], vmin=0, vmax=max_val, cmap="PuOr") +plt.title("ADMM-SBTV (Y-Z) view", fontsize=19) plt.colorbar(ax=ax4) plt.show() -#plt.savefig('SBTV_phantom.pdf', format='pdf', dpi=1600) +# plt.savefig('SBTV_phantom.pdf', format='pdf', dpi=1600) -# calculate errors +# calculate errors Qtools = QualityTools(phantom, RecADMM_reg_sbtv) RMSE_admm_sbtv = Qtools.rmse() print("Root Mean Square Error for ADMM-SB-TV is {}".format(RMSE_admm_sbtv)) # SSIM measure -Qtools = QualityTools(phantom[128,:,:]*255, RecADMM_reg_sbtv[128,:,:]*235) +Qtools = QualityTools(phantom[128, :, :] * 255, RecADMM_reg_sbtv[128, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_admm_sbtv = Qtools.ssim(win2d) print("Mean SSIM ADMM-SBTV is {}".format(ssim_admm_sbtv[0])) -#%% -print ("Reconstructing with ADMM method using ROFLLT penalty") -RecADMM_reg_rofllt = RectoolsIR.ADMM(projdata_norm, - rho_const = 2000.0, \ - iterationsADMM = 25, \ - regularisation = 'LLT_ROF', \ - regularisation_parameter = optimReg_rofllt,\ - regularisation_parameter2 = 0.0085,\ - regularisation_iterations = 600) +# %% +print("Reconstructing with ADMM method using ROFLLT penalty") +RecADMM_reg_rofllt = RectoolsIR.ADMM( + projdata_norm, + rho_const=2000.0, + iterationsADMM=25, + regularisation="LLT_ROF", + regularisation_parameter=optimReg_rofllt, + regularisation_parameter2=0.0085, + regularisation_iterations=600, +) -sliceSel = int(0.5*N_size) +sliceSel = int(0.5 * N_size) max_val = 1 -plt.figure(figsize = (20,3)) +plt.figure(figsize=(20, 3)) gs1 = gridspec.GridSpec(1, 4) -gs1.update(wspace=0.02, hspace=0.01) # set the spacing between axes. +gs1.update(wspace=0.02, hspace=0.01) # set the spacing between axes. ax1 = plt.subplot(gs1[0]) -plt.plot(reg_param_rofllt_vec, erros_vec_rofllt, color='k',linewidth=2) -plt.xlabel('Regularisation parameter', fontsize=16) -plt.ylabel('RMSE value', fontsize=16) -plt.title('Regularisation selection', fontsize=19) +plt.plot(reg_param_rofllt_vec, erros_vec_rofllt, color="k", linewidth=2) +plt.xlabel("Regularisation parameter", fontsize=16) +plt.ylabel("RMSE value", fontsize=16) +plt.title("Regularisation selection", fontsize=19) ax2 = plt.subplot(gs1[1]) -plt.imshow(RecADMM_reg_rofllt[sliceSel,:,:],vmin=0, vmax=max_val, cmap="PuOr") -ax2.plot([x0, x1], [y0, y1], 'ko-', linestyle='--') -plt.title('ADMM-ROFLLT (X-Y) view', fontsize=19) -#ax2.set_aspect('equal') +plt.imshow(RecADMM_reg_rofllt[sliceSel, :, :], vmin=0, vmax=max_val, cmap="PuOr") +ax2.plot([x0, x1], [y0, y1], "ko-", linestyle="--") +plt.title("ADMM-ROFLLT (X-Y) view", fontsize=19) +# ax2.set_aspect('equal') ax3 = plt.subplot(gs1[2]) -plt.plot(phantom[sliceSel,sliceSel,0:N_size],color='k',linewidth=2) -plt.plot(RecADMM_reg_rofllt[sliceSel,sliceSel,0:N_size],linestyle='--',color='g') -plt.title('Profile', fontsize=19) +plt.plot(phantom[sliceSel, sliceSel, 0:N_size], color="k", linewidth=2) +plt.plot(RecADMM_reg_rofllt[sliceSel, sliceSel, 0:N_size], linestyle="--", color="g") +plt.title("Profile", fontsize=19) ax4 = plt.subplot(gs1[3]) -plt.imshow(RecADMM_reg_rofllt[:,sliceSel,:],vmin=0, vmax=max_val, cmap="PuOr") -plt.title('ADMM-ROFLLT (Y-Z) view', fontsize=19) +plt.imshow(RecADMM_reg_rofllt[:, sliceSel, :], vmin=0, vmax=max_val, cmap="PuOr") +plt.title("ADMM-ROFLLT (Y-Z) view", fontsize=19) plt.colorbar(ax=ax4) plt.show() -#plt.savefig('ROFLLT_phantom.pdf', format='pdf', dpi=1600) +# plt.savefig('ROFLLT_phantom.pdf', format='pdf', dpi=1600) -# calculate errors +# calculate errors Qtools = QualityTools(phantom, RecADMM_reg_rofllt) RMSE_admm_rofllt = Qtools.rmse() print("Root Mean Square Error for ADMM-ROF-LLT is {}".format(RMSE_admm_rofllt)) # SSIM measure -Qtools = QualityTools(phantom[128,:,:]*255, RecADMM_reg_rofllt[128,:,:]*235) +Qtools = QualityTools(phantom[128, :, :] * 255, RecADMM_reg_rofllt[128, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_admm_rifllt = Qtools.ssim(win2d) print("Mean SSIM ADMM-ROFLLT is {}".format(ssim_admm_rifllt[0])) -#%% -print ("Reconstructing with ADMM method using TGV penalty") -RecADMM_reg_tgv = RectoolsIR.ADMM(projdata_norm, - rho_const = 2000.0, \ - iterationsADMM = 25, \ - regularisation = 'TGV', \ - regularisation_parameter = optimReg_tgv,\ - regularisation_iterations = 600) -#%% -sliceSel = int(0.5*N_size) +# %% +print("Reconstructing with ADMM method using TGV penalty") +RecADMM_reg_tgv = RectoolsIR.ADMM( + projdata_norm, + rho_const=2000.0, + iterationsADMM=25, + regularisation="TGV", + regularisation_parameter=optimReg_tgv, + regularisation_iterations=600, +) +# %% +sliceSel = int(0.5 * N_size) max_val = 1 -plt.figure(figsize = (20,3)) +plt.figure(figsize=(20, 3)) gs1 = gridspec.GridSpec(1, 4) -gs1.update(wspace=0.02, hspace=0.01) # set the spacing between axes. +gs1.update(wspace=0.02, hspace=0.01) # set the spacing between axes. ax1 = plt.subplot(gs1[0]) -plt.plot(reg_param_tgv_vec, erros_vec_tgv, color='k',linewidth=2) -plt.xlabel('Regularisation parameter', fontsize=16) -plt.ylabel('RMSE value', fontsize=16) -plt.title('Regularisation selection', fontsize=19) +plt.plot(reg_param_tgv_vec, erros_vec_tgv, color="k", linewidth=2) +plt.xlabel("Regularisation parameter", fontsize=16) +plt.ylabel("RMSE value", fontsize=16) +plt.title("Regularisation selection", fontsize=19) ax2 = plt.subplot(gs1[1]) -plt.imshow(RecADMM_reg_tgv[sliceSel,:,:],vmin=0, vmax=max_val, cmap="PuOr") -ax2.plot([x0, x1], [y0, y1], 'ko-', linestyle='--') -plt.title('ADMM-TGV (X-Y) view', fontsize=19) -#ax2.set_aspect('equal') +plt.imshow(RecADMM_reg_tgv[sliceSel, :, :], vmin=0, vmax=max_val, cmap="PuOr") +ax2.plot([x0, x1], [y0, y1], "ko-", linestyle="--") +plt.title("ADMM-TGV (X-Y) view", fontsize=19) +# ax2.set_aspect('equal') ax3 = plt.subplot(gs1[2]) -plt.plot(phantom[sliceSel,sliceSel,0:N_size],color='k',linewidth=2) -plt.plot(RecADMM_reg_tgv[sliceSel,sliceSel,0:N_size],linestyle='--',color='g') -plt.title('Profile', fontsize=19) +plt.plot(phantom[sliceSel, sliceSel, 0:N_size], color="k", linewidth=2) +plt.plot(RecADMM_reg_tgv[sliceSel, sliceSel, 0:N_size], linestyle="--", color="g") +plt.title("Profile", fontsize=19) ax4 = plt.subplot(gs1[3]) -plt.imshow(RecADMM_reg_tgv[:,sliceSel,:],vmin=0, vmax=max_val, cmap="PuOr") -plt.title('ADMM-TGV (Y-Z) view', fontsize=19) +plt.imshow(RecADMM_reg_tgv[:, sliceSel, :], vmin=0, vmax=max_val, cmap="PuOr") +plt.title("ADMM-TGV (Y-Z) view", fontsize=19) plt.colorbar(ax=ax4) plt.show() -#plt.savefig('TGV_phantom.pdf', format='pdf', dpi=1600) +# plt.savefig('TGV_phantom.pdf', format='pdf', dpi=1600) -# calculate errors +# calculate errors Qtools = QualityTools(phantom, RecADMM_reg_tgv) RMSE_admm_tgv = Qtools.rmse() print("Root Mean Square Error for ADMM-TGV is {}".format(RMSE_admm_tgv)) # SSIM measure -#Create a 2d gaussian for the window parameter -Qtools = QualityTools(phantom[128,:,:]*255, RecADMM_reg_tgv[128,:,:]*235) +# Create a 2d gaussian for the window parameter +Qtools = QualityTools(phantom[128, :, :] * 255, RecADMM_reg_tgv[128, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_admm_tgv = Qtools.ssim(win2d) print("Mean SSIM ADMM-TGV is {}".format(ssim_admm_tgv[0])) -#%% +# %% diff --git a/demos/SoftwareX_supp/Demo_SimulData_SX.py b/demos/SoftwareX_supp/Demo_SimulData_SX.py index cdf4325..01e4dfa 100644 --- a/demos/SoftwareX_supp/Demo_SimulData_SX.py +++ b/demos/SoftwareX_supp/Demo_SimulData_SX.py @@ -26,92 +26,108 @@ from tomophantom.supp.flatsgen import flats from tomophantom.supp.normraw import normaliser_sim -print ("Building 3D phantom using TomoPhantom software") -tic=timeit.default_timer() -model = 16 # select a model number from the library -N_size = 256 # Define phantom dimensions using a scalar value (cubic phantom) +print("Building 3D phantom using TomoPhantom software") +tic = timeit.default_timer() +model = 16 # select a model number from the library +N_size = 256 # Define phantom dimensions using a scalar value (cubic phantom) path = os.path.dirname(tomophantom.__file__) path_library3D = os.path.join(path, "Phantom3DLibrary.dat") -#This will generate a N_size x N_size x N_size phantom (3D) +# This will generate a N_size x N_size x N_size phantom (3D) phantom_tm = TomoP3D.Model(model, N_size, path_library3D) -toc=timeit.default_timer() +toc = timeit.default_timer() Run_time = toc - tic print("Phantom has been built in {} seconds".format(Run_time)) -sliceSel = int(0.5*N_size) -#plt.gray() -plt.figure() +sliceSel = int(0.5 * N_size) +# plt.gray() +plt.figure() plt.subplot(131) -plt.imshow(phantom_tm[sliceSel,:,:],vmin=0, vmax=1) -plt.title('3D Phantom, axial view') +plt.imshow(phantom_tm[sliceSel, :, :], vmin=0, vmax=1) +plt.title("3D Phantom, axial view") plt.subplot(132) -plt.imshow(phantom_tm[:,sliceSel,:],vmin=0, vmax=1) -plt.title('3D Phantom, coronal view') +plt.imshow(phantom_tm[:, sliceSel, :], vmin=0, vmax=1) +plt.title("3D Phantom, coronal view") plt.subplot(133) -plt.imshow(phantom_tm[:,:,sliceSel],vmin=0, vmax=1) -plt.title('3D Phantom, sagittal view') +plt.imshow(phantom_tm[:, :, sliceSel], vmin=0, vmax=1) +plt.title("3D Phantom, sagittal view") plt.show() # Projection geometry related parameters: -Horiz_det = int(np.sqrt(2)*N_size) # detector column count (horizontal) -Vert_det = N_size # detector row count (vertical) (no reason for it to be > N) -angles_num = int(0.35*np.pi*N_size); # angles number -angles = np.linspace(0.0,179.9,angles_num,dtype='float32') # in degrees -angles_rad = angles*(np.pi/180.0) -#%% -print ("Building 3D analytical projection data with TomoPhantom") -projData3D_analyt= TomoP3D.ModelSino(model, N_size, Horiz_det, Vert_det, angles, path_library3D) +Horiz_det = int(np.sqrt(2) * N_size) # detector column count (horizontal) +Vert_det = N_size # detector row count (vertical) (no reason for it to be > N) +angles_num = int(0.35 * np.pi * N_size) +# angles number +angles = np.linspace(0.0, 179.9, angles_num, dtype="float32") # in degrees +angles_rad = angles * (np.pi / 180.0) +# %% +print("Building 3D analytical projection data with TomoPhantom") +projData3D_analyt = TomoP3D.ModelSino( + model, N_size, Horiz_det, Vert_det, angles, path_library3D +) intens_max = N_size -sliceSel = int(0.5*N_size) -plt.figure() +sliceSel = int(0.5 * N_size) +plt.figure() plt.subplot(131) -plt.imshow(projData3D_analyt[:,sliceSel,:],vmin=0, vmax=intens_max) -plt.title('2D Projection (analytical)') +plt.imshow(projData3D_analyt[:, sliceSel, :], vmin=0, vmax=intens_max) +plt.title("2D Projection (analytical)") plt.subplot(132) -plt.imshow(projData3D_analyt[sliceSel,:,:],vmin=0, vmax=intens_max) -plt.title('Sinogram view') +plt.imshow(projData3D_analyt[sliceSel, :, :], vmin=0, vmax=intens_max) +plt.title("Sinogram view") plt.subplot(133) -plt.imshow(projData3D_analyt[:,:,sliceSel],vmin=0, vmax=intens_max) -plt.title('Tangentogram view') +plt.imshow(projData3D_analyt[:, :, sliceSel], vmin=0, vmax=intens_max) +plt.title("Tangentogram view") plt.show() -#%% -print ("Simulate flat fields, add noise and normalise projections...") -flatsnum = 20 # generate 20 flat fields -flatsSIM = flats(Vert_det, Horiz_det, maxheight = 0.1, maxthickness = 3, sigma_noise = 0.2, sigmasmooth = 3, flatsnum=flatsnum) +# %% +print("Simulate flat fields, add noise and normalise projections...") +flatsnum = 20 # generate 20 flat fields +flatsSIM = flats( + Vert_det, + Horiz_det, + maxheight=0.1, + maxthickness=3, + sigma_noise=0.2, + sigmasmooth=3, + flatsnum=flatsnum, +) -plt.figure() -plt.imshow(flatsSIM[0,:,:],vmin=0, vmax=1) -plt.title('A selected simulated flat-field') -#%% +plt.figure() +plt.imshow(flatsSIM[0, :, :], vmin=0, vmax=1) +plt.title("A selected simulated flat-field") +# %% # Apply normalisation of data and add noise -flux_intensity = 60000 # controls the level of noise -sigma_flats = 0.01 # contro the level of noise in flats (higher creates more ring artifacts) -projData3D_norm = normaliser_sim(projData3D_analyt, flatsSIM, sigma_flats, flux_intensity) +flux_intensity = 60000 # controls the level of noise +sigma_flats = ( + 0.01 # contro the level of noise in flats (higher creates more ring artifacts) +) +projData3D_norm = normaliser_sim( + projData3D_analyt, flatsSIM, sigma_flats, flux_intensity +) intens_max = N_size -sliceSel = int(0.5*N_size) -plt.figure() +sliceSel = int(0.5 * N_size) +plt.figure() plt.subplot(131) -plt.imshow(projData3D_norm[:,sliceSel,:],vmin=0, vmax=intens_max) -plt.title('2D Projection (erroneous)') +plt.imshow(projData3D_norm[:, sliceSel, :], vmin=0, vmax=intens_max) +plt.title("2D Projection (erroneous)") plt.subplot(132) -plt.imshow(projData3D_norm[sliceSel,:,:],vmin=0, vmax=intens_max) -plt.title('Sinogram view') +plt.imshow(projData3D_norm[sliceSel, :, :], vmin=0, vmax=intens_max) +plt.title("Sinogram view") plt.subplot(133) -plt.imshow(projData3D_norm[:,:,sliceSel],vmin=0, vmax=intens_max) -plt.title('Tangentogram view') +plt.imshow(projData3D_norm[:, :, sliceSel], vmin=0, vmax=intens_max) +plt.title("Tangentogram view") plt.show() -#%% +# %% import h5py import time + time_label = int(time.time()) # Saving generated data with a unique time label -h5f = h5py.File('TomoSim_data'+str(time_label)+'.h5', 'w') -h5f.create_dataset('phantom', data=phantom_tm) -h5f.create_dataset('projdata_norm', data=projData3D_norm) -h5f.create_dataset('proj_angles', data=angles_rad) +h5f = h5py.File("TomoSim_data" + str(time_label) + ".h5", "w") +h5f.create_dataset("phantom", data=phantom_tm) +h5f.create_dataset("projdata_norm", data=projData3D_norm) +h5f.create_dataset("proj_angles", data=angles_rad) h5f.close() -#%% \ No newline at end of file +# %% diff --git a/demos/SoftwareX_supp/Demo_VolumeDenoise.py b/demos/SoftwareX_supp/Demo_VolumeDenoise.py index e128127..d54238e 100644 --- a/demos/SoftwareX_supp/Demo_VolumeDenoise.py +++ b/demos/SoftwareX_supp/Demo_VolumeDenoise.py @@ -17,6 +17,7 @@ """ import timeit import matplotlib.pyplot as plt + # import matplotlib.gridspec as gridspec import numpy as np import os @@ -26,478 +27,613 @@ from ccpi.supp.qualitymetrics import QualityTools from scipy.signal import gaussian from ccpi.filters.regularisers import ROF_TV, FGP_TV, SB_TV, LLT_ROF, TGV, NDF, Diff4th -#%% -print ("Building 3D phantom using TomoPhantom software") -tic=timeit.default_timer() -model = 16 # select a model number from the library -N_size = 128 # Define phantom dimensions using a scalar value (cubic phantom) + +# %% +print("Building 3D phantom using TomoPhantom software") +tic = timeit.default_timer() +model = 16 # select a model number from the library +N_size = 128 # Define phantom dimensions using a scalar value (cubic phantom) path = os.path.dirname(tomophantom.__file__) path_library3D = os.path.join(path, "Phantom3DLibrary.dat") -#This will generate a N_size x N_size x N_size phantom (3D) +# This will generate a N_size x N_size x N_size phantom (3D) phantom_tm = TomoP3D.Model(model, N_size, path_library3D) -toc=timeit.default_timer() +toc = timeit.default_timer() Run_time = toc - tic print("Phantom has been built in {} seconds".format(Run_time)) # adding normally distributed noise artifacts_add = ArtifactsClass(phantom_tm) -phantom_noise = artifacts_add.noise(sigma=0.1,noisetype='Gaussian') +phantom_noise = artifacts_add.noise(sigma=0.1, noisetype="Gaussian") -sliceSel = int(0.5*N_size) -#plt.gray() -plt.figure() +sliceSel = int(0.5 * N_size) +# plt.gray() +plt.figure() plt.subplot(131) -plt.imshow(phantom_noise[sliceSel,:,:],vmin=0, vmax=1.4) -plt.title('3D Phantom, axial view') +plt.imshow(phantom_noise[sliceSel, :, :], vmin=0, vmax=1.4) +plt.title("3D Phantom, axial view") plt.subplot(132) -plt.imshow(phantom_noise[:,sliceSel,:],vmin=0, vmax=1.4) -plt.title('3D Phantom, coronal view') +plt.imshow(phantom_noise[:, sliceSel, :], vmin=0, vmax=1.4) +plt.title("3D Phantom, coronal view") plt.subplot(133) -plt.imshow(phantom_noise[:,:,sliceSel],vmin=0, vmax=1.4) -plt.title('3D Phantom, sagittal view') +plt.imshow(phantom_noise[:, :, sliceSel], vmin=0, vmax=1.4) +plt.title("3D Phantom, sagittal view") plt.show() -#%% -print ("____________________Applying regularisers_______________________") -print ("________________________________________________________________") +# %% +print("____________________Applying regularisers_______________________") +print("________________________________________________________________") -print ("#############ROF TV CPU####################") +print("#############ROF TV CPU####################") # set parameters -pars = {'algorithm': ROF_TV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06,\ - 'number_of_iterations': 1000,\ - 'time_marching_parameter': 0.00025,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(rof_cpu3D, infcpu) = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],'cpu') - -toc=timeit.default_timer() +pars = { + "algorithm": ROF_TV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "number_of_iterations": 1000, + "time_marching_parameter": 0.00025, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(rof_cpu3D, infcpu) = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + "cpu", +) + +toc = timeit.default_timer() Run_time_rof = toc - tic Qtools = QualityTools(phantom_tm, rof_cpu3D) RMSE_rof = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, rof_cpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, rof_cpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_rof = Qtools.ssim(win2d) -print("ROF-TV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE_rof,ssim_rof[0],Run_time_rof)) -#%% -print ("#############ROF TV GPU####################") +print( + "ROF-TV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE_rof, ssim_rof[0], Run_time_rof + ) +) +# %% +print("#############ROF TV GPU####################") # set parameters -pars = {'algorithm': ROF_TV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06,\ - 'number_of_iterations': 8330,\ - 'time_marching_parameter': 0.00025,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(rof_gpu3D, infogpu) = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],'gpu') - -toc=timeit.default_timer() +pars = { + "algorithm": ROF_TV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "number_of_iterations": 8330, + "time_marching_parameter": 0.00025, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(rof_gpu3D, infogpu) = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + "gpu", +) + +toc = timeit.default_timer() Run_time_rof = toc - tic Qtools = QualityTools(phantom_tm, rof_gpu3D) RMSE_rof = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, rof_gpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, rof_gpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_rof = Qtools.ssim(win2d) -print("ROF-TV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE_rof,ssim_rof[0],Run_time_rof)) -#%% -print ("#############FGP TV CPU####################") +print( + "ROF-TV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE_rof, ssim_rof[0], Run_time_rof + ) +) +# %% +print("#############FGP TV CPU####################") # set parameters -pars = {'algorithm' : FGP_TV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06, \ - 'number_of_iterations' : 930 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -tic=timeit.default_timer() -(fgp_cpu3D, infoFGP) = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'],'cpu') -toc=timeit.default_timer() +pars = { + "algorithm": FGP_TV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "number_of_iterations": 930, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, +} + +tic = timeit.default_timer() +(fgp_cpu3D, infoFGP) = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + "cpu", +) +toc = timeit.default_timer() Run_time_fgp = toc - tic Qtools = QualityTools(phantom_tm, fgp_cpu3D) RMSE_rof = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, fgp_cpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, fgp_cpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_fgp = Qtools.ssim(win2d) -print("FGP-TV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE_rof,ssim_fgp[0],Run_time_fgp)) -#%% -print ("#############FGP TV GPU####################") +print( + "FGP-TV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE_rof, ssim_fgp[0], Run_time_fgp + ) +) +# %% +print("#############FGP TV GPU####################") # set parameters -pars = {'algorithm' : FGP_TV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06, \ - 'number_of_iterations' :930 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -tic=timeit.default_timer() -(fgp_gpu3D,infogpu) = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'],'gpu') -toc=timeit.default_timer() +pars = { + "algorithm": FGP_TV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "number_of_iterations": 930, + "tolerance_constant": 0.0, + "methodTV": 0, + "nonneg": 0, +} + +tic = timeit.default_timer() +(fgp_gpu3D, infogpu) = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + "gpu", +) +toc = timeit.default_timer() Run_time_fgp = toc - tic Qtools = QualityTools(phantom_tm, fgp_gpu3D) RMSE_rof = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, fgp_gpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, fgp_gpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim_fgp = Qtools.ssim(win2d) -print("FGP-TV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE_rof,ssim_fgp[0],Run_time_fgp)) -#%% -print ("#############SB TV CPU####################") +print( + "FGP-TV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE_rof, ssim_fgp[0], Run_time_fgp + ) +) +# %% +print("#############SB TV CPU####################") # set parameters -pars = {'algorithm' : SB_TV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06, \ - 'number_of_iterations' :225 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0} - -tic=timeit.default_timer() -(sb_cpu3D, info_vec_cpu) = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], 'cpu') -toc=timeit.default_timer() +pars = { + "algorithm": SB_TV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "number_of_iterations": 225, + "tolerance_constant": 0.0, + "methodTV": 0, +} + +tic = timeit.default_timer() +(sb_cpu3D, info_vec_cpu) = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + "cpu", +) +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, sb_cpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, sb_cpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, sb_cpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("SB-TV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############SB TV GPU####################") +print( + "SB-TV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############SB TV GPU####################") # set parameters -pars = {'algorithm' : SB_TV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06, \ - 'number_of_iterations' :225 ,\ - 'tolerance_constant':0.0,\ - 'methodTV': 0} - -tic=timeit.default_timer() -(sb_gpu3D,info_vec_gpu) = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], 'gpu') - -toc=timeit.default_timer() +pars = { + "algorithm": SB_TV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "number_of_iterations": 225, + "tolerance_constant": 0.0, + "methodTV": 0, +} + +tic = timeit.default_timer() +(sb_gpu3D, info_vec_gpu) = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + "gpu", +) + +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, sb_gpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, sb_gpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, sb_gpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("SB-TV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############NDF CPU####################") +print( + "SB-TV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############NDF CPU####################") # set parameters -pars = {'algorithm' : NDF, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06, \ - 'edge_parameter':0.017,\ - 'number_of_iterations' :530 ,\ - 'time_marching_parameter':0.01,\ - 'penalty_type':1,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(ndf_cpu3D, info_vec_cpu) = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'],'cpu') -toc=timeit.default_timer() +pars = { + "algorithm": NDF, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "edge_parameter": 0.017, + "number_of_iterations": 530, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(ndf_cpu3D, info_vec_cpu) = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + "cpu", +) +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, ndf_cpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, ndf_cpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, ndf_cpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("NDF (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############NDF GPU####################") +print( + "NDF (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############NDF GPU####################") # set parameters -pars = {'algorithm' : NDF, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06, \ - 'edge_parameter':0.017,\ - 'number_of_iterations' :530 ,\ - 'time_marching_parameter':0.01,\ - 'penalty_type':1,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(ndf_gpu3D,info_vec_gpu) = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'],'gpu') - -toc=timeit.default_timer() +pars = { + "algorithm": NDF, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "edge_parameter": 0.017, + "number_of_iterations": 530, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(ndf_gpu3D, info_vec_gpu) = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + "gpu", +) + +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, ndf_gpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, ndf_gpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, ndf_gpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("NDF (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############Diff4th CPU####################") +print( + "NDF (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############Diff4th CPU####################") # set parameters -pars = {'algorithm' : Diff4th, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':4.5, \ - 'edge_parameter':0.035,\ - 'number_of_iterations' :2425 ,\ - 'time_marching_parameter':0.001,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(diff4th_cpu3D, info_vec_cpu) = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],'cpu') -toc=timeit.default_timer() +pars = { + "algorithm": Diff4th, + "input": phantom_noise, + "regularisation_parameter": 4.5, + "edge_parameter": 0.035, + "number_of_iterations": 2425, + "time_marching_parameter": 0.001, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(diff4th_cpu3D, info_vec_cpu) = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + "cpu", +) +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, diff4th_cpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, diff4th_cpu3D[sliceSel,:,:]*235) +Qtools = QualityTools( + phantom_tm[sliceSel, :, :] * 255, diff4th_cpu3D[sliceSel, :, :] * 235 +) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("Diff4th (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############Diff4th GPU####################") +print( + "Diff4th (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############Diff4th GPU####################") # set parameters -pars = {'algorithm' : Diff4th, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':4.5, \ - 'edge_parameter':0.035,\ - 'number_of_iterations' :2425 ,\ - 'time_marching_parameter':0.001,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(diff4th_gpu3D,info_vec_gpu) = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],'gpu') - -toc=timeit.default_timer() +pars = { + "algorithm": Diff4th, + "input": phantom_noise, + "regularisation_parameter": 4.5, + "edge_parameter": 0.035, + "number_of_iterations": 2425, + "time_marching_parameter": 0.001, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(diff4th_gpu3D, info_vec_gpu) = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + "gpu", +) + +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, diff4th_gpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, diff4th_gpu3D[sliceSel,:,:]*235) +Qtools = QualityTools( + phantom_tm[sliceSel, :, :] * 255, diff4th_gpu3D[sliceSel, :, :] * 235 +) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("Diff4th (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############TGV CPU####################") +print( + "Diff4th (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############TGV CPU####################") # set parameters -pars = {'algorithm' : TGV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06,\ - 'alpha1':1.0,\ - 'alpha0':2.0,\ - 'number_of_iterations' :1000,\ - 'LipshitzConstant' :12,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(tgv_cpu3D, info_vec_cpu) = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'],'cpu') -toc=timeit.default_timer() +pars = { + "algorithm": TGV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "alpha1": 1.0, + "alpha0": 2.0, + "number_of_iterations": 1000, + "LipshitzConstant": 12, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(tgv_cpu3D, info_vec_cpu) = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + "cpu", +) +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, tgv_cpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, tgv_cpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, tgv_cpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("TGV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############TGV GPU####################") +print( + "TGV (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############TGV GPU####################") # set parameters -pars = {'algorithm' : TGV, \ - 'input' : phantom_noise,\ - 'regularisation_parameter':0.06,\ - 'alpha1':1.0,\ - 'alpha0':2.0,\ - 'number_of_iterations' :7845,\ - 'LipshitzConstant' :12,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(tgv_gpu3D,info_vec_gpu) = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'],'gpu') - -toc=timeit.default_timer() +pars = { + "algorithm": TGV, + "input": phantom_noise, + "regularisation_parameter": 0.06, + "alpha1": 1.0, + "alpha0": 2.0, + "number_of_iterations": 7845, + "LipshitzConstant": 12, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(tgv_gpu3D, info_vec_gpu) = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + "gpu", +) + +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, tgv_gpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, tgv_gpu3D[sliceSel,:,:]*235) +Qtools = QualityTools(phantom_tm[sliceSel, :, :] * 255, tgv_gpu3D[sliceSel, :, :] * 235) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("TGV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############ROF-LLT CPU####################") +print( + "TGV (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############ROF-LLT CPU####################") # set parameters -pars = {'algorithm' : LLT_ROF, \ - 'input' : phantom_noise,\ - 'regularisation_parameterROF':0.03, \ - 'regularisation_parameterLLT':0.015, \ - 'number_of_iterations' : 1000 ,\ - 'time_marching_parameter' :0.00025 ,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(rofllt_cpu3D, info_vec_cpu) = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], 'cpu') -toc=timeit.default_timer() +pars = { + "algorithm": LLT_ROF, + "input": phantom_noise, + "regularisation_parameterROF": 0.03, + "regularisation_parameterLLT": 0.015, + "number_of_iterations": 1000, + "time_marching_parameter": 0.00025, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(rofllt_cpu3D, info_vec_cpu) = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + "cpu", +) +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, rofllt_cpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, rofllt_cpu3D[sliceSel,:,:]*235) +Qtools = QualityTools( + phantom_tm[sliceSel, :, :] * 255, rofllt_cpu3D[sliceSel, :, :] * 235 +) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("ROF-LLT (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) -#%% -print ("#############ROF-LLT GPU####################") +print( + "ROF-LLT (cpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) +# %% +print("#############ROF-LLT GPU####################") # set parameters -pars = {'algorithm' : LLT_ROF, \ - 'input' : phantom_noise,\ - 'regularisation_parameterROF':0.03, \ - 'regularisation_parameterLLT':0.015, \ - 'number_of_iterations' : 8000 ,\ - 'time_marching_parameter' :0.00025 ,\ - 'tolerance_constant':0.0} - -tic=timeit.default_timer() -(rofllt_gpu3D,info_vec_gpu) = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], 'gpu') -toc=timeit.default_timer() +pars = { + "algorithm": LLT_ROF, + "input": phantom_noise, + "regularisation_parameterROF": 0.03, + "regularisation_parameterLLT": 0.015, + "number_of_iterations": 8000, + "time_marching_parameter": 0.00025, + "tolerance_constant": 0.0, +} + +tic = timeit.default_timer() +(rofllt_gpu3D, info_vec_gpu) = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + "gpu", +) +toc = timeit.default_timer() Run_time = toc - tic Qtools = QualityTools(phantom_tm, rofllt_gpu3D) RMSE = Qtools.rmse() # SSIM measure -Qtools = QualityTools(phantom_tm[sliceSel,:,:]*255, rofllt_gpu3D[sliceSel,:,:]*235) +Qtools = QualityTools( + phantom_tm[sliceSel, :, :] * 255, rofllt_gpu3D[sliceSel, :, :] * 235 +) win = np.array([gaussian(11, 1.5)]) win2d = win * (win.T) ssim = Qtools.ssim(win2d) -print("ROF-LLT (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format(RMSE,ssim[0],Run_time)) +print( + "ROF-LLT (gpu) ____ RMSE: {}, MMSIM: {}, run time: {} sec".format( + RMSE, ssim[0], Run_time + ) +) diff --git a/demos/data/binary_save.py b/demos/data/binary_save.py index 06b5eb2..b38b437 100644 --- a/demos/data/binary_save.py +++ b/demos/data/binary_save.py @@ -1,11 +1,12 @@ import numpy as np -outfile = open('test_imageLena.bin', 'wb') # Open a file for binary write + +outfile = open("test_imageLena.bin", "wb") # Open a file for binary write outfile.write(Im) # Write it outfile.flush() # Optional but a good idea outfile.close() -#%% +# %% # Define width and height w, h = 256, 256 # Read file using numpy "fromfile()" -with open('my_file.bin', mode='rb') as f: - d = np.fromfile(f,dtype=np.float32,count=w*h).reshape(h,w) \ No newline at end of file +with open("my_file.bin", mode="rb") as f: + d = np.fromfile(f, dtype=np.float32, count=w * h).reshape(h, w) diff --git a/demos/demo_cpu_regularisers.py b/demos/demo_cpu_regularisers.py index 9a9f090..df4cd83 100644 --- a/demos/demo_cpu_regularisers.py +++ b/demos/demo_cpu_regularisers.py @@ -14,586 +14,729 @@ import timeit from imageio.v2 import imread -from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, TNV, NDF, Diff4th +from ccpi.filters.regularisers import ( + ROF_TV, + FGP_TV, + PD_TV, + SB_TV, + TGV, + LLT_ROF, + FGP_dTV, + TNV, + NDF, + Diff4th, +) from ccpi.filters.regularisers import PatchSelect, NLTV from ccpi.supp.qualitymetrics import QualityTools + + ############################################################################### def printParametersToString(pars): - txt = r'' - for key, value in pars.items(): - if key== 'algorithm' : - txt += "{0} = {1}".format(key, value.__name__) - elif key == 'input': - txt += "{0} = {1}".format(key, np.shape(value)) - elif key == 'refdata': - txt += "{0} = {1}".format(key, np.shape(value)) - else: - txt += "{0} = {1}".format(key, value) - txt += '\n' - return txt + txt = r"" + for key, value in pars.items(): + if key == "algorithm": + txt += "{0} = {1}".format(key, value.__name__) + elif key == "input": + txt += "{0} = {1}".format(key, np.shape(value)) + elif key == "refdata": + txt += "{0} = {1}".format(key, np.shape(value)) + else: + txt += "{0} = {1}".format(key, value) + txt += "\n" + return txt + + ############################################################################### -filename = os.path.join( "../test/test_data" ,"peppers.tif") +filename = os.path.join("../test/test_data", "peppers.tif") # read image Im = imread(filename) -Im = Im/255.0 +Im = Im / 255.0 perc = 0.08 -u0 = Im + np.random.normal(loc = 0 , - scale = perc * Im , - size = np.shape(Im)) -u_ref = Im + np.random.normal(loc = 0 , - scale = 0.01 * Im , - size = np.shape(Im)) -(N,M) = np.shape(u0) +u0 = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) +u_ref = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) +(N, M) = np.shape(u0) # map the u0 u0->u0>0 # f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) -u0 = u0.astype('float32') -u_ref = u_ref.astype('float32') +u0 = u0.astype("float32") +u_ref = u_ref.astype("float32") plt.figure() plt.imshow(u0, cmap="gray") plt.show() -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________ROF-TV (2D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________ROF-TV (2D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of ROF-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of ROF-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm': ROF_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02,\ - 'number_of_iterations': 4000,\ - 'time_marching_parameter': 0.001,\ - 'tolerance_constant':1e-06} - -info_vec = np.zeros(2, dtype = np.float32) -print ("#############ROF TV CPU####################") +pars = { + "algorithm": ROF_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 4000, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +info_vec = np.zeros(2, dtype=np.float32) +print("#############ROF TV CPU####################") start_time = timeit.default_timer() -rof_cpu = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device = 'cpu', infovector=info_vec) +rof_cpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + infovector=info_vec, +) Qtools = QualityTools(Im, rof_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(rof_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________FGP-TV (2D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +plt.title("{}".format("CPU results")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________FGP-TV (2D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of FGP-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of FGP-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : FGP_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1500 ,\ - 'tolerance_constant':1e-08,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP TV CPU####################") +pars = { + "algorithm": FGP_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 1500, + "tolerance_constant": 1e-08, + "methodTV": 0, + "nonneg": 0, +} + +print("#############FGP TV CPU####################") start_time = timeit.default_timer() -fgp_cpu = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'], device ='cpu') +fgp_cpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="cpu", +) Qtools = QualityTools(Im, fgp_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(fgp_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) +plt.title("{}".format("CPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________PD-TV (2D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________PD-TV (2D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of PD-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of PD-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : PD_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1500 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 ,\ - 'nonneg': 1, - 'lipschitz_const' : 8} - -print ("#############PD TV CPU####################") +pars = { + "algorithm": PD_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 1500, + "tolerance_constant": 1e-06, + "methodTV": 0, + "nonneg": 1, + "lipschitz_const": 8, +} + +print("#############PD TV CPU####################") start_time = timeit.default_timer() -pd_cpu = PD_TV(pars["input"], - pars["regularisation_parameter"], - pars["number_of_iterations"], - pars["tolerance_constant"], - pars["lipschitz_const"], - pars["methodTV"], - pars["nonneg"], - device="cpu", - ) +pd_cpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", +) Qtools = QualityTools(Im, pd_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(pd_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________SB-TV (2D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +plt.title("{}".format("CPU results")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________SB-TV (2D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of SB-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of SB-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : SB_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :100 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0} - -print ("#############SB TV CPU####################") +pars = { + "algorithm": SB_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 100, + "tolerance_constant": 1e-06, + "methodTV": 0, +} + +print("#############SB TV CPU####################") start_time = timeit.default_timer() -sb_cpu = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], device='cpu') - -#Qtools = QualityTools(Im, sb_cpu) -#pars['rmse'] = Qtools.rmse() +sb_cpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="cpu", +) + +# Qtools = QualityTools(Im, sb_cpu) +# pars['rmse'] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(sb_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) -#%% +plt.title("{}".format("CPU results")) +# %% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("______________LLT- ROF (2D)________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("______________LLT- ROF (2D)________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of LLT-ROF regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of LLT-ROF regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : LLT_ROF, \ - 'input' : u0,\ - 'regularisation_parameterROF':0.01, \ - 'regularisation_parameterLLT':0.0085, \ - 'number_of_iterations' :6000 ,\ - 'time_marching_parameter' :0.001 ,\ - 'tolerance_constant':1e-06} - -print ("#############LLT- ROF CPU####################") +pars = { + "algorithm": LLT_ROF, + "input": u0, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.0085, + "number_of_iterations": 6000, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############LLT- ROF CPU####################") start_time = timeit.default_timer() -lltrof_cpu = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device = 'cpu') +lltrof_cpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", +) Qtools = QualityTools(Im, lltrof_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(lltrof_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) +plt.title("{}".format("CPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_____Total Generalised Variation (2D)______") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_____Total Generalised Variation (2D)______") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of TGV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of TGV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : TGV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'alpha1':1.0,\ - 'alpha0':2.0,\ - 'number_of_iterations' :1000 ,\ - 'LipshitzConstant' :12 ,\ - 'tolerance_constant':1e-06} - -print ("#############TGV CPU####################") +pars = { + "algorithm": TGV, + "input": u0, + "regularisation_parameter": 0.02, + "alpha1": 1.0, + "alpha0": 2.0, + "number_of_iterations": 1000, + "LipshitzConstant": 12, + "tolerance_constant": 1e-06, +} + +print("#############TGV CPU####################") start_time = timeit.default_timer() -(tgv_cpu,info_vec_cpu) = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'], 'cpu') +(tgv_cpu, info_vec_cpu) = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + "cpu", +) Qtools = QualityTools(Im, tgv_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(tgv_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) +plt.title("{}".format("CPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("________________NDF (2D)___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("________________NDF (2D)___________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of NDF regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of NDF regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : NDF, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'edge_parameter':0.017,\ - 'number_of_iterations' :1500 ,\ - 'time_marching_parameter':0.01,\ - 'penalty_type':1,\ - 'tolerance_constant':1e-06} - -print ("#############NDF CPU################") +pars = { + "algorithm": NDF, + "input": u0, + "regularisation_parameter": 0.02, + "edge_parameter": 0.017, + "number_of_iterations": 1500, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 1e-06, +} + +print("#############NDF CPU################") start_time = timeit.default_timer() -(ndf_cpu,info_vec_cpu) = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'],'cpu') - +(ndf_cpu, info_vec_cpu) = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + "cpu", +) + Qtools = QualityTools(Im, ndf_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(ndf_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) +plt.title("{}".format("CPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Anisotropic Diffusion 4th Order (2D)____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Anisotropic Diffusion 4th Order (2D)____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of Diff4th regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of Diff4th regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : Diff4th, \ - 'input' : u0,\ - 'regularisation_parameter':0.8, \ - 'edge_parameter':0.02,\ - 'number_of_iterations' :5500 ,\ - 'time_marching_parameter':0.001,\ - 'tolerance_constant':1e-06} - -print ("#############Diff4th CPU################") +pars = { + "algorithm": Diff4th, + "input": u0, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 5500, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############Diff4th CPU################") start_time = timeit.default_timer() -(diff4_cpu,info_vec_cpu) = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],'cpu') - +(diff4_cpu, info_vec_cpu) = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + "cpu", +) + Qtools = QualityTools(Im, diff4_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(diff4_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) +plt.title("{}".format("CPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Nonlocal patches pre-calculation____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Nonlocal patches pre-calculation____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") start_time = timeit.default_timer() # set parameters -pars = {'algorithm' : PatchSelect, \ - 'input' : u0,\ - 'searchwindow': 7, \ - 'patchwindow': 2,\ - 'neighbours' : 15 ,\ - 'edge_parameter':0.18} - -H_i, H_j, Weights = PatchSelect(pars['input'], - pars['searchwindow'], - pars['patchwindow'], - pars['neighbours'], - pars['edge_parameter'],'cpu') - +pars = { + "algorithm": PatchSelect, + "input": u0, + "searchwindow": 7, + "patchwindow": 2, + "neighbours": 15, + "edge_parameter": 0.18, +} + +H_i, H_j, Weights = PatchSelect( + pars["input"], + pars["searchwindow"], + pars["patchwindow"], + pars["neighbours"], + pars["edge_parameter"], + "cpu", +) + txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) """ plt.figure() plt.imshow(Weights[0,:,:],cmap="gray",interpolation="nearest",vmin=0, vmax=1) plt.show() """ -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Nonlocal Total Variation penalty____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Nonlocal Total Variation penalty____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +## plot fig = plt.figure() -plt.suptitle('Performance of NLTV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -pars2 = {'algorithm' : NLTV, \ - 'input' : u0,\ - 'H_i': H_i, \ - 'H_j': H_j,\ - 'H_k' : 0,\ - 'Weights' : Weights,\ - 'regularisation_parameter': 0.04,\ - 'iterations': 3 - } +plt.suptitle("Performance of NLTV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") + +pars2 = { + "algorithm": NLTV, + "input": u0, + "H_i": H_i, + "H_j": H_j, + "H_k": 0, + "Weights": Weights, + "regularisation_parameter": 0.04, + "iterations": 3, +} start_time = timeit.default_timer() -nltv_cpu = NLTV(pars2['input'], - pars2['H_i'], - pars2['H_j'], - pars2['H_k'], - pars2['Weights'], - pars2['regularisation_parameter'], - pars2['iterations']) +nltv_cpu = NLTV( + pars2["input"], + pars2["H_i"], + pars2["H_j"], + pars2["H_k"], + pars2["Weights"], + pars2["regularisation_parameter"], + pars2["iterations"], +) Qtools = QualityTools(Im, nltv_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(nltv_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) +plt.title("{}".format("CPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_____________FGP-dTV (2D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_____________FGP-dTV (2D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of FGP-dTV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of FGP-dTV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : FGP_dTV, \ - 'input' : u0,\ - 'refdata' : u_ref,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :500 ,\ - 'tolerance_constant':1e-06,\ - 'eta_const':0.2,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP dTV CPU####################") +pars = { + "algorithm": FGP_dTV, + "input": u0, + "refdata": u_ref, + "regularisation_parameter": 0.02, + "number_of_iterations": 500, + "tolerance_constant": 1e-06, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, +} + +print("#############FGP dTV CPU####################") start_time = timeit.default_timer() -(fgp_dtv_cpu,info_vec_cpu) = FGP_dTV(pars['input'], - pars['refdata'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['eta_const'], - pars['methodTV'], - pars['nonneg'],'cpu') - +(fgp_dtv_cpu, info_vec_cpu) = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + "cpu", +) + Qtools = QualityTools(Im, fgp_dtv_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(fgp_dtv_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("__________Total nuclear Variation__________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +plt.title("{}".format("CPU results")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("__________Total nuclear Variation__________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of TNV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of TNV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") channelsNo = 5 -noisyVol = np.zeros((channelsNo,N,M),dtype='float32') -idealVol = np.zeros((channelsNo,N,M),dtype='float32') +noisyVol = np.zeros((channelsNo, N, M), dtype="float32") +idealVol = np.zeros((channelsNo, N, M), dtype="float32") -for i in range (channelsNo): - noisyVol[i,:,:] = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) - idealVol[i,:,:] = Im +for i in range(channelsNo): + noisyVol[i, :, :] = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) + idealVol[i, :, :] = Im # set parameters -pars = {'algorithm' : TNV, \ - 'input' : noisyVol,\ - 'regularisation_parameter': 0.04, \ - 'number_of_iterations' : 200 ,\ - 'tolerance_constant':1e-05 - } - -print ("#############TNV CPU#################") +pars = { + "algorithm": TNV, + "input": noisyVol, + "regularisation_parameter": 0.04, + "number_of_iterations": 200, + "tolerance_constant": 1e-05, +} + +print("#############TNV CPU#################") start_time = timeit.default_timer() -tnv_cpu = TNV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant']) +tnv_cpu = TNV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], +) Qtools = QualityTools(idealVol, tnv_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(tnv_cpu[3,:,:], cmap="gray") -plt.title('{}'.format('CPU results')) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(tnv_cpu[3, :, :], cmap="gray") +plt.title("{}".format("CPU results")) diff --git a/demos/demo_cpu_regularisers3D.py b/demos/demo_cpu_regularisers3D.py index 203881b..1f2b3bc 100644 --- a/demos/demo_cpu_regularisers3D.py +++ b/demos/demo_cpu_regularisers3D.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -#%% +# %% """ Created on Thu Feb 22 11:39:43 2018 @@ -15,481 +15,607 @@ import timeit from imageio.v2 import imread -from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th +from ccpi.filters.regularisers import ( + ROF_TV, + FGP_TV, + PD_TV, + SB_TV, + TGV, + LLT_ROF, + FGP_dTV, + NDF, + Diff4th, +) from ccpi.supp.qualitymetrics import QualityTools + + ############################################################################### def printParametersToString(pars): - txt = r'' - for key, value in pars.items(): - if key== 'algorithm' : - txt += "{0} = {1}".format(key, value.__name__) - elif key == 'input': - txt += "{0} = {1}".format(key, np.shape(value)) - elif key == 'refdata': - txt += "{0} = {1}".format(key, np.shape(value)) - else: - txt += "{0} = {1}".format(key, value) - txt += '\n' - return txt + txt = r"" + for key, value in pars.items(): + if key == "algorithm": + txt += "{0} = {1}".format(key, value.__name__) + elif key == "input": + txt += "{0} = {1}".format(key, np.shape(value)) + elif key == "refdata": + txt += "{0} = {1}".format(key, np.shape(value)) + else: + txt += "{0} = {1}".format(key, value) + txt += "\n" + return txt + + ############################################################################### -filename = os.path.join( "../test/test_data" ,"peppers.tif") +filename = os.path.join("../test/test_data", "peppers.tif") # read image Im = imread(filename) -Im = Im/255.0 +Im = Im / 255.0 perc = 0.05 -u0 = Im + np.random.normal(loc = 0 , - scale = perc * Im , - size = np.shape(Im)) -u_ref = Im + np.random.normal(loc = 0 , - scale = 0.01 * Im , - size = np.shape(Im)) -(N,M) = np.shape(u0) +u0 = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) +u_ref = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) +(N, M) = np.shape(u0) -u0 = u0.astype('float32') -u_ref = u_ref.astype('float32') +u0 = u0.astype("float32") +u_ref = u_ref.astype("float32") slices = 20 -noisyVol = np.zeros((slices,N,M),dtype='float32') -noisyRef = np.zeros((slices,N,M),dtype='float32') -idealVol = np.zeros((slices,N,M),dtype='float32') +noisyVol = np.zeros((slices, N, M), dtype="float32") +noisyRef = np.zeros((slices, N, M), dtype="float32") +idealVol = np.zeros((slices, N, M), dtype="float32") -for i in range (slices): - noisyVol[i,:,:] = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) - noisyRef[i,:,:] = Im + np.random.normal(loc = 0 , scale = 0.01 * Im , size = np.shape(Im)) - idealVol[i,:,:] = Im +for i in range(slices): + noisyVol[i, :, :] = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) + noisyRef[i, :, :] = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) + idealVol[i, :, :] = Im -info_vec_cpu = np.zeros(2, dtype = np.float32) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________ROF-TV (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +info_vec_cpu = np.zeros(2, dtype=np.float32) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________ROF-TV (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of ROF-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) +plt.suptitle("Performance of ROF-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) slice_num = 10 -a.set_title(f'Noisy slice {slice_num} of a volume') -imgplot = plt.imshow(noisyVol[slice_num,:,:],cmap="gray") +a.set_title(f"Noisy slice {slice_num} of a volume") +imgplot = plt.imshow(noisyVol[slice_num, :, :], cmap="gray") # set parameters -pars = {'algorithm': ROF_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.08,\ - 'number_of_iterations': 200,\ - 'time_marching_parameter': 0.0007,\ - 'tolerance_constant':1e-06} - -print ("#############ROF TV CPU####################") +pars = { + "algorithm": ROF_TV, + "input": noisyVol, + "regularisation_parameter": 0.08, + "number_of_iterations": 200, + "time_marching_parameter": 0.0007, + "tolerance_constant": 1e-06, +} + +print("#############ROF TV CPU####################") start_time = timeit.default_timer() -info_vec_cpu = np.zeros(2, dtype = np.float32) -rof_cpu3D = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='cpu', infovector=info_vec_cpu) +info_vec_cpu = np.zeros(2, dtype=np.float32) +rof_cpu3D = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + infovector=info_vec_cpu, +) Qtools = QualityTools(idealVol, rof_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(rof_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using ROF-TV')) - -print (f"information vector: {info_vec_cpu}") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________FGP-TV (3D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(rof_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using ROF-TV")) + +print(f"information vector: {info_vec_cpu}") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________FGP-TV (3D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of FGP-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of FGP-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : FGP_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter': 0.08,# 0.02 * 10000, - 'number_of_iterations' :2000 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP TV GPU####################") +pars = { + "algorithm": FGP_TV, + "input": noisyVol, + "regularisation_parameter": 0.08, # 0.02 * 10000, + "number_of_iterations": 2000, + "tolerance_constant": 1e-06, + "methodTV": 0, + "nonneg": 0, +} + +print("#############FGP TV GPU####################") start_time = timeit.default_timer() -fgp_cpu3D = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'], device='cpu', infovector=info_vec_cpu) - +fgp_cpu3D = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + infovector=info_vec_cpu, +) + Qtools = QualityTools(idealVol, fgp_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using FGP-TV')) - -#%% -imgplot = plt.imshow(fgp_cpu3D[:,1,:], cmap="gray") -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________PD-TV (3D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(fgp_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using FGP-TV")) + +# %% +imgplot = plt.imshow(fgp_cpu3D[:, 1, :], cmap="gray") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________PD-TV (3D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of PD-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of PD-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : PD_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1000 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 ,\ - 'nonneg': 0, - 'lipschitz_const' : 8} - -print ("#############PD-TV (3D) CPU####################") +pars = { + "algorithm": PD_TV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "number_of_iterations": 1000, + "tolerance_constant": 1e-06, + "methodTV": 0, + "nonneg": 0, + "lipschitz_const": 8, +} + +print("#############PD-TV (3D) CPU####################") start_time = timeit.default_timer() -pd_cpu3D = PD_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['lipschitz_const'], - pars['methodTV'], - pars['nonneg'], - device='cpu', infovector=info_vec_cpu) - +pd_cpu3D = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + infovector=info_vec_cpu, +) + Qtools = QualityTools(idealVol, pd_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(pd_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using PD-TV')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________SB-TV (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(pd_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using PD-TV")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________SB-TV (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of SB-TV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of SB-TV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : SB_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :250 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0} - -print ("#############SB TV CPU####################") +pars = { + "algorithm": SB_TV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "number_of_iterations": 250, + "tolerance_constant": 1e-06, + "methodTV": 0, +} + +print("#############SB TV CPU####################") start_time = timeit.default_timer() -sb_cpu3D = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], device='cpu', infovector=info_vec_cpu) +sb_cpu3D = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="cpu", + infovector=info_vec_cpu, +) Qtools = QualityTools(idealVol, sb_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(sb_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using SB-TV')) - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________LLT-ROF (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(sb_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using SB-TV")) + +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________LLT-ROF (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of LLT-ROF regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of LLT-ROF regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : LLT_ROF, \ - 'input' : noisyVol,\ - 'regularisation_parameterROF':0.01, \ - 'regularisation_parameterLLT':0.008, \ - 'number_of_iterations' :500 ,\ - 'time_marching_parameter' :0.001 ,\ - 'tolerance_constant':1e-06} - -print ("#############LLT ROF CPU####################") +pars = { + "algorithm": LLT_ROF, + "input": noisyVol, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.008, + "number_of_iterations": 500, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############LLT ROF CPU####################") start_time = timeit.default_timer() -lltrof_cpu3D = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='cpu', infovector=info_vec_cpu) +lltrof_cpu3D = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + infovector=info_vec_cpu, +) Qtools = QualityTools(idealVol, lltrof_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(lltrof_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using LLT-ROF')) - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________TGV (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(lltrof_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using LLT-ROF")) + +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________TGV (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of TGV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of TGV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : TGV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'alpha1':1.0,\ - 'alpha0':2.0,\ - 'number_of_iterations' :500 ,\ - 'LipshitzConstant' :12 ,\ - 'tolerance_constant':1e-06} - -print ("#############TGV CPU####################") +pars = { + "algorithm": TGV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "alpha1": 1.0, + "alpha0": 2.0, + "number_of_iterations": 500, + "LipshitzConstant": 12, + "tolerance_constant": 1e-06, +} + +print("#############TGV CPU####################") start_time = timeit.default_timer() -tgv_cpu3D = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'], device='cpu', infovector=info_vec_cpu) +tgv_cpu3D = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + device="cpu", + infovector=info_vec_cpu, +) Qtools = QualityTools(idealVol, tgv_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(tgv_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using TGV')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("________________NDF (3D)___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(tgv_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using TGV")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("________________NDF (3D)___________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of NDF regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy volume') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of NDF regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy volume") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : NDF, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'edge_parameter':0.015,\ - 'number_of_iterations' :700 ,\ - 'time_marching_parameter':0.01,\ - 'penalty_type': 1,\ - 'tolerance_constant':1e-06} - -print ("#############NDF CPU################") +pars = { + "algorithm": NDF, + "input": noisyVol, + "regularisation_parameter": 0.02, + "edge_parameter": 0.015, + "number_of_iterations": 700, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 1e-06, +} + +print("#############NDF CPU################") start_time = timeit.default_timer() -ndf_cpu3D = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'], device='cpu', infovector=info_vec_cpu) - +ndf_cpu3D = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="cpu", + infovector=info_vec_cpu, +) + Qtools = QualityTools(idealVol, ndf_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(ndf_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using NDF iterations')) - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Anisotropic Diffusion 4th Order (2D)____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(ndf_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using NDF iterations")) + +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Anisotropic Diffusion 4th Order (2D)____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of Diff4th regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy volume') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of Diff4th regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy volume") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : Diff4th, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.8, \ - 'edge_parameter':0.02,\ - 'number_of_iterations' :500 ,\ - 'time_marching_parameter':0.001,\ - 'tolerance_constant':1e-06} - -print ("#############Diff4th CPU################") +pars = { + "algorithm": Diff4th, + "input": noisyVol, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 500, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############Diff4th CPU################") start_time = timeit.default_timer() -diff4th_cpu3D = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='cpu', infovector=info_vec_cpu) - +diff4th_cpu3D = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="cpu", + infovector=info_vec_cpu, +) + Qtools = QualityTools(idealVol, diff4th_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(diff4th_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using DIFF4th iterations')) - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________FGP-dTV (3D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(diff4th_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using DIFF4th iterations")) + +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________FGP-dTV (3D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of FGP-dTV regulariser using the CPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of FGP-dTV regulariser using the CPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : FGP_dTV,\ - 'input' : noisyVol,\ - 'refdata' : noisyRef,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :500 ,\ - 'tolerance_constant':1e-06,\ - 'eta_const':0.2,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP dTV CPU####################") +pars = { + "algorithm": FGP_dTV, + "input": noisyVol, + "refdata": noisyRef, + "regularisation_parameter": 0.02, + "number_of_iterations": 500, + "tolerance_constant": 1e-06, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, +} + +print("#############FGP dTV CPU####################") start_time = timeit.default_timer() -fgp_dTV_cpu3D = FGP_dTV(pars['input'], - pars['refdata'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['eta_const'], - pars['methodTV'], - pars['nonneg'], device='cpu', infovector=info_vec_cpu) - +fgp_dTV_cpu3D = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="cpu", + infovector=info_vec_cpu, +) + Qtools = QualityTools(idealVol, fgp_dTV_cpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_dTV_cpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the CPU using FGP-dTV')) -#%% +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(fgp_dTV_cpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the CPU using FGP-dTV")) +# %% diff --git a/demos/demo_gpu_regularisers.py b/demos/demo_gpu_regularisers.py index a93e3c3..24861ea 100644 --- a/demos/demo_gpu_regularisers.py +++ b/demos/demo_gpu_regularisers.py @@ -14,535 +14,676 @@ import timeit from imageio.v2 import imread -from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th +from ccpi.filters.regularisers import ( + ROF_TV, + FGP_TV, + PD_TV, + SB_TV, + TGV, + LLT_ROF, + FGP_dTV, + NDF, + Diff4th, +) from ccpi.filters.regularisers import PatchSelect, NLTV from ccpi.supp.qualitymetrics import QualityTools + + ############################################################################### def printParametersToString(pars): - txt = r'' - for key, value in pars.items(): - if key== 'algorithm' : - txt += "{0} = {1}".format(key, value.__name__) - elif key == 'input': - txt += "{0} = {1}".format(key, np.shape(value)) - elif key == 'refdata': - txt += "{0} = {1}".format(key, np.shape(value)) - else: - txt += "{0} = {1}".format(key, value) - txt += '\n' - return txt + txt = r"" + for key, value in pars.items(): + if key == "algorithm": + txt += "{0} = {1}".format(key, value.__name__) + elif key == "input": + txt += "{0} = {1}".format(key, np.shape(value)) + elif key == "refdata": + txt += "{0} = {1}".format(key, np.shape(value)) + else: + txt += "{0} = {1}".format(key, value) + txt += "\n" + return txt + + ############################################################################### -filename = os.path.join( "../test/test_data" ,"peppers.tif") +filename = os.path.join("../test/test_data", "peppers.tif") # read image Im = imread(filename) -Im = Im/255.0 +Im = Im / 255.0 perc = 0.08 -u0 = Im + np.random.normal(loc = 0 , - scale = perc * Im , - size = np.shape(Im)) -u_ref = Im + np.random.normal(loc = 0 , - scale = 0.01 * Im , - size = np.shape(Im)) -(N,M) = np.shape(u0) +u0 = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) +u_ref = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) +(N, M) = np.shape(u0) # map the u0 u0->u0>0 # f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) -u0 = u0.astype('float32') -u_ref = u_ref.astype('float32') +u0 = u0.astype("float32") +u_ref = u_ref.astype("float32") plt.figure() plt.imshow(u0, cmap="gray") plt.show() -#%% +# %% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________ROF-TV regulariser_____________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("____________ROF-TV regulariser_____________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of the ROF-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of the ROF-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm': ROF_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02,\ - 'number_of_iterations': 4000,\ - 'time_marching_parameter': 0.001,\ - 'tolerance_constant':1e-06} - -print ("##############ROF TV GPU##################") +pars = { + "algorithm": ROF_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 4000, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("##############ROF TV GPU##################") start_time = timeit.default_timer() -info_vec_gpu = np.zeros(2, dtype = np.float32) -rof_gpu = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='gpu', infovector=info_vec_gpu) +info_vec_gpu = np.zeros(2, dtype=np.float32) +rof_gpu = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, rof_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = ROF_TV +pars["rmse"] = Qtools.rmse() +pars["algorithm"] = ROF_TV txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(rof_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) +plt.title("{}".format("GPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________FGP-TV regulariser_____________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("____________FGP-TV regulariser_____________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of the FGP-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of the FGP-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : FGP_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :400 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("##############FGP TV GPU##################") +pars = { + "algorithm": FGP_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 400, + "tolerance_constant": 1e-06, + "methodTV": 0, + "nonneg": 0, +} + +print("##############FGP TV GPU##################") start_time = timeit.default_timer() -fgp_gpu= FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'],device='gpu', infovector=info_vec_gpu) +fgp_gpu = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, fgp_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = FGP_TV +pars["rmse"] = Qtools.rmse() +pars["algorithm"] = FGP_TV txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(fgp_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________PD-TV (2D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +plt.title("{}".format("GPU results")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________PD-TV (2D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of PD-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of PD-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : PD_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1500 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 ,\ - 'nonneg': 1, - 'lipschitz_const' : 8} - -print ("#############PD TV GPU####################") +pars = { + "algorithm": PD_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 1500, + "tolerance_constant": 1e-06, + "methodTV": 0, + "nonneg": 1, + "lipschitz_const": 8, +} + +print("#############PD TV GPU####################") start_time = timeit.default_timer() -pd_gpu= PD_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['lipschitz_const'], - pars['methodTV'], - pars['nonneg'], - device='gpu', infovector=info_vec_gpu) +pd_gpu = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, pd_gpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(pd_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________SB-TV regulariser______________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +plt.title("{}".format("GPU results")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("____________SB-TV regulariser______________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of the SB-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of the SB-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : SB_TV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :250 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0} - -print ("##############SB TV GPU##################") +pars = { + "algorithm": SB_TV, + "input": u0, + "regularisation_parameter": 0.02, + "number_of_iterations": 250, + "tolerance_constant": 1e-06, + "methodTV": 0, +} + +print("##############SB TV GPU##################") start_time = timeit.default_timer() -sb_gpu = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], device='gpu', infovector=info_vec_gpu) +sb_gpu = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, sb_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = SB_TV +pars["rmse"] = Qtools.rmse() +pars["algorithm"] = SB_TV txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(sb_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) -#%% +plt.title("{}".format("GPU results")) +# %% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("______________LLT- ROF (2D)________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("______________LLT- ROF (2D)________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of LLT-ROF regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of LLT-ROF regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : LLT_ROF, \ - 'input' : u0,\ - 'regularisation_parameterROF':0.01, \ - 'regularisation_parameterLLT':0.0085, \ - 'number_of_iterations' : 6000 ,\ - 'time_marching_parameter' :0.001 ,\ - 'tolerance_constant':1e-06} - -print ("#############LLT- ROF GPU####################") +pars = { + "algorithm": LLT_ROF, + "input": u0, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.0085, + "number_of_iterations": 6000, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############LLT- ROF GPU####################") start_time = timeit.default_timer() -lltrof_gpu = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='gpu', infovector=info_vec_gpu) - +lltrof_gpu = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) + Qtools = QualityTools(Im, lltrof_gpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(lltrof_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) +plt.title("{}".format("GPU results")) -#%% +# %% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_____Total Generalised Variation (2D)______") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_____Total Generalised Variation (2D)______") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of TGV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of TGV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : TGV, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'alpha1':1.0,\ - 'alpha0':2.0,\ - 'number_of_iterations' :1000 ,\ - 'LipshitzConstant' :12 ,\ - 'tolerance_constant':1e-06} - -print ("#############TGV GPU####################") +pars = { + "algorithm": TGV, + "input": u0, + "regularisation_parameter": 0.02, + "alpha1": 1.0, + "alpha0": 2.0, + "number_of_iterations": 1000, + "LipshitzConstant": 12, + "tolerance_constant": 1e-06, +} + +print("#############TGV GPU####################") start_time = timeit.default_timer() -tgv_gpu = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'],device='gpu', infovector=info_vec_gpu) +tgv_gpu = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, tgv_gpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(tgv_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) +plt.title("{}".format("GPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________NDF regulariser_____________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________NDF regulariser_____________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of the NDF regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of the NDF regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : NDF, \ - 'input' : u0,\ - 'regularisation_parameter':0.02, \ - 'edge_parameter':0.017,\ - 'number_of_iterations' :1500 ,\ - 'time_marching_parameter':0.01,\ - 'penalty_type':1,\ - 'tolerance_constant':1e-06} - -print ("##############NDF GPU##################") +pars = { + "algorithm": NDF, + "input": u0, + "regularisation_parameter": 0.02, + "edge_parameter": 0.017, + "number_of_iterations": 1500, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 1e-06, +} + +print("##############NDF GPU##################") start_time = timeit.default_timer() -ndf_gpu = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'],device='gpu', infovector=info_vec_gpu) +ndf_gpu = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, ndf_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = NDF +pars["rmse"] = Qtools.rmse() +pars["algorithm"] = NDF txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(ndf_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) +plt.title("{}".format("GPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Anisotropic Diffusion 4th Order (2D)____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Anisotropic Diffusion 4th Order (2D)____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of Diff4th regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of Diff4th regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : Diff4th, \ - 'input' : u0,\ - 'regularisation_parameter':0.8, \ - 'edge_parameter':0.02,\ - 'number_of_iterations' :5500 ,\ - 'time_marching_parameter':0.001,\ - 'tolerance_constant':1e-06} - -print ("#############DIFF4th GPU################") +pars = { + "algorithm": Diff4th, + "input": u0, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 5500, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############DIFF4th GPU################") start_time = timeit.default_timer() -diff4_gpu = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],device='gpu', infovector=info_vec_gpu) +diff4_gpu = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, diff4_gpu) -pars['algorithm'] = Diff4th -pars['rmse'] = Qtools.rmse() +pars["algorithm"] = Diff4th +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(diff4_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) +plt.title("{}".format("GPU results")) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Nonlocal patches pre-calculation____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Nonlocal patches pre-calculation____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") start_time = timeit.default_timer() # set parameters -pars = {'algorithm' : PatchSelect, \ - 'input' : u0,\ - 'searchwindow': 7, \ - 'patchwindow': 2,\ - 'neighbours' : 15 ,\ - 'edge_parameter':0.18} - -H_i, H_j, Weights = PatchSelect(pars['input'], - pars['searchwindow'], - pars['patchwindow'], - pars['neighbours'], - pars['edge_parameter'],device='gpu') - +pars = { + "algorithm": PatchSelect, + "input": u0, + "searchwindow": 7, + "patchwindow": 2, + "neighbours": 15, + "edge_parameter": 0.18, +} + +H_i, H_j, Weights = PatchSelect( + pars["input"], + pars["searchwindow"], + pars["patchwindow"], + pars["neighbours"], + pars["edge_parameter"], + device="gpu", +) + txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) """ plt.figure() plt.imshow(Weights[0,:,:],cmap="gray",interpolation="nearest",vmin=0, vmax=1) plt.show() """ -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Nonlocal Total Variation penalty____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Nonlocal Total Variation penalty____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +## plot fig = plt.figure() -plt.suptitle('Performance of NLTV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") - -pars2 = {'algorithm' : NLTV, \ - 'input' : u0,\ - 'H_i': H_i, \ - 'H_j': H_j,\ - 'H_k' : 0,\ - 'Weights' : Weights,\ - 'regularisation_parameter': 0.02,\ - 'iterations': 3, - 'neighbours': 15, - } +plt.suptitle("Performance of NLTV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") + +pars2 = { + "algorithm": NLTV, + "input": u0, + "H_i": H_i, + "H_j": H_j, + "H_k": 0, + "Weights": Weights, + "regularisation_parameter": 0.02, + "iterations": 3, + "neighbours": 15, +} start_time = timeit.default_timer() -nltv_cpu = NLTV(pars2['input'], - pars2['H_i'], - pars2['H_j'], - pars2['H_k'], - pars2['Weights'], - pars2['neighbours'], - pars2['regularisation_parameter'], - pars2['iterations']) +nltv_cpu = NLTV( + pars2["input"], + pars2["H_i"], + pars2["H_j"], + pars2["H_k"], + pars2["Weights"], + pars2["neighbours"], + pars2["regularisation_parameter"], + pars2["iterations"], +) Qtools = QualityTools(Im, nltv_cpu) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(nltv_cpu, cmap="gray") -plt.title('{}'.format('CPU results')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("____________FGP-dTV bench___________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +plt.title("{}".format("CPU results")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("____________FGP-dTV bench___________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of the FGP-dTV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(u0,cmap="gray") +plt.suptitle("Performance of the FGP-dTV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(u0, cmap="gray") # set parameters -pars = {'algorithm' : FGP_dTV, \ - 'input' : u0,\ - 'refdata' : u_ref,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :500 ,\ - 'tolerance_constant':1e-06,\ - 'eta_const':0.2,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("##############FGP dTV GPU##################") +pars = { + "algorithm": FGP_dTV, + "input": u0, + "refdata": u_ref, + "regularisation_parameter": 0.02, + "number_of_iterations": 500, + "tolerance_constant": 1e-06, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, +} + +print("##############FGP dTV GPU##################") start_time = timeit.default_timer() -fgp_dtv_gpu = FGP_dTV(pars['input'], - pars['refdata'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['eta_const'], - pars['methodTV'], - pars['nonneg'],device='gpu', infovector=info_vec_gpu) +fgp_dtv_gpu = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(Im, fgp_dtv_gpu) -pars['rmse'] = Qtools.rmse() -pars['algorithm'] = FGP_dTV +pars["rmse"] = Qtools.rmse() +pars["algorithm"] = FGP_dTV txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) imgplot = plt.imshow(fgp_dtv_gpu, cmap="gray") -plt.title('{}'.format('GPU results')) -#%% \ No newline at end of file +plt.title("{}".format("GPU results")) +# %% diff --git a/demos/demo_gpu_regularisers3D.py b/demos/demo_gpu_regularisers3D.py index 5e0c759..8b938a7 100644 --- a/demos/demo_gpu_regularisers3D.py +++ b/demos/demo_gpu_regularisers3D.py @@ -15,465 +15,591 @@ from imageio.v2 import imread -from ccpi.filters.regularisers import ROF_TV, FGP_TV, PD_TV, SB_TV, TGV, LLT_ROF, FGP_dTV, NDF, Diff4th +from ccpi.filters.regularisers import ( + ROF_TV, + FGP_TV, + PD_TV, + SB_TV, + TGV, + LLT_ROF, + FGP_dTV, + NDF, + Diff4th, +) from ccpi.supp.qualitymetrics import QualityTools + + ############################################################################### def printParametersToString(pars): - txt = r'' - for key, value in pars.items(): - if key== 'algorithm' : - txt += "{0} = {1}".format(key, value.__name__) - elif key == 'input': - txt += "{0} = {1}".format(key, np.shape(value)) - elif key == 'refdata': - txt += "{0} = {1}".format(key, np.shape(value)) - else: - txt += "{0} = {1}".format(key, value) - txt += '\n' - return txt + txt = r"" + for key, value in pars.items(): + if key == "algorithm": + txt += "{0} = {1}".format(key, value.__name__) + elif key == "input": + txt += "{0} = {1}".format(key, np.shape(value)) + elif key == "refdata": + txt += "{0} = {1}".format(key, np.shape(value)) + else: + txt += "{0} = {1}".format(key, value) + txt += "\n" + return txt + + ############################################################################### -#%% -filename = os.path.join( "../test/test_data" ,"peppers.tif") +# %% +filename = os.path.join("../test/test_data", "peppers.tif") # read image Im = imread(filename) -Im = Im/255.0 +Im = Im / 255.0 perc = 0.05 -u0 = Im + np.random.normal(loc = 0 , - scale = perc * Im , - size = np.shape(Im)) -u_ref = Im + np.random.normal(loc = 0 , - scale = 0.01 * Im , - size = np.shape(Im)) -(N,M) = np.shape(u0) +u0 = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) +u_ref = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) +(N, M) = np.shape(u0) # map the u0 u0->u0>0 # f = np.frompyfunc(lambda x: 0 if x < 0 else x, 1,1) -u0 = u0.astype('float32') -u_ref = u_ref.astype('float32') +u0 = u0.astype("float32") +u_ref = u_ref.astype("float32") slices = 20 -noisyVol = np.zeros((slices,N,N),dtype='float32') -noisyRef = np.zeros((slices,N,N),dtype='float32') -idealVol = np.zeros((slices,N,N),dtype='float32') +noisyVol = np.zeros((slices, N, N), dtype="float32") +noisyRef = np.zeros((slices, N, N), dtype="float32") +idealVol = np.zeros((slices, N, N), dtype="float32") -for i in range (slices): - noisyVol[i,:,:] = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) - noisyRef[i,:,:] = Im + np.random.normal(loc = 0 , scale = 0.01 * Im , size = np.shape(Im)) - idealVol[i,:,:] = Im +for i in range(slices): + noisyVol[i, :, :] = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) + noisyRef[i, :, :] = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) + idealVol[i, :, :] = Im -info_vec_gpu = np.zeros((2,), dtype='float32') -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________ROF-TV (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +info_vec_gpu = np.zeros((2,), dtype="float32") +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________ROF-TV (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of ROF-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy 15th slice of a volume') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of ROF-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy 15th slice of a volume") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm': ROF_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02,\ - 'number_of_iterations': 7000,\ - 'time_marching_parameter': 0.0007,\ - 'tolerance_constant':1e-06} - -print ("#############ROF TV GPU####################") +pars = { + "algorithm": ROF_TV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "number_of_iterations": 7000, + "time_marching_parameter": 0.0007, + "tolerance_constant": 1e-06, +} + +print("#############ROF TV GPU####################") start_time = timeit.default_timer() -rof_gpu3D = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='gpu', infovector=info_vec_gpu) +rof_gpu3D = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, rof_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(rof_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using ROF-TV')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________FGP-TV (3D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(rof_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using ROF-TV")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________FGP-TV (3D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of FGP-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of FGP-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : FGP_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1000 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP TV GPU####################") +pars = { + "algorithm": FGP_TV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "number_of_iterations": 1000, + "tolerance_constant": 1e-06, + "methodTV": 0, + "nonneg": 0, +} + +print("#############FGP TV GPU####################") start_time = timeit.default_timer() -fgp_gpu3D = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'], device='gpu', infovector=info_vec_gpu) +fgp_gpu3D = FGP_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, fgp_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using FGP-TV')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________PD-TV (3D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(fgp_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using FGP-TV")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________PD-TV (3D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of PD-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of PD-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : PD_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :1000 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 ,\ - 'nonneg': 0, - 'lipschitz_const' : 8} - -print ("#############PD TV GPU####################") +pars = { + "algorithm": PD_TV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "number_of_iterations": 1000, + "tolerance_constant": 1e-06, + "methodTV": 0, + "nonneg": 0, + "lipschitz_const": 8, +} + +print("#############PD TV GPU####################") start_time = timeit.default_timer() -pd_gpu3D = PD_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['lipschitz_const'], - pars['methodTV'], - pars['nonneg'], - device='gpu', infovector=info_vec_gpu) +pd_gpu3D = PD_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["lipschitz_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, pd_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(pd_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using PD-TV')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________SB-TV (3D)__________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(pd_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using PD-TV")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________SB-TV (3D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of SB-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of SB-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : SB_TV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'number_of_iterations' :300 ,\ - 'tolerance_constant':1e-06,\ - 'methodTV': 0 } - -print ("#############SB TV GPU####################") +pars = { + "algorithm": SB_TV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "number_of_iterations": 300, + "tolerance_constant": 1e-06, + "methodTV": 0, +} + +print("#############SB TV GPU####################") start_time = timeit.default_timer() -sb_gpu3D = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'],device='gpu', infovector=info_vec_gpu) +sb_gpu3D = SB_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["methodTV"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, sb_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(sb_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using SB-TV')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________LLT-ROF (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(sb_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using SB-TV")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________LLT-ROF (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of LLT-ROF regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of LLT-ROF regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : LLT_ROF, \ - 'input' : noisyVol,\ - 'regularisation_parameterROF':0.01, \ - 'regularisation_parameterLLT':0.008, \ - 'number_of_iterations' : 500 ,\ - 'time_marching_parameter' :0.001 ,\ - 'tolerance_constant':1e-06} - -print ("#############LLT ROF GPU####################") +pars = { + "algorithm": LLT_ROF, + "input": noisyVol, + "regularisation_parameterROF": 0.01, + "regularisation_parameterLLT": 0.008, + "number_of_iterations": 500, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############LLT ROF GPU####################") start_time = timeit.default_timer() -lltrof_gpu3D = LLT_ROF(pars['input'], - pars['regularisation_parameterROF'], - pars['regularisation_parameterLLT'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], device='gpu', infovector=info_vec_gpu) +lltrof_gpu3D = LLT_ROF( + pars["input"], + pars["regularisation_parameterROF"], + pars["regularisation_parameterLLT"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, lltrof_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(lltrof_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using LLT-ROF')) - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________TGV (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(lltrof_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using LLT-ROF")) + +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________TGV (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of TGV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of TGV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : TGV, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'alpha1':1.0,\ - 'alpha0':2.0,\ - 'number_of_iterations' :500 ,\ - 'LipshitzConstant' :12 ,\ - 'tolerance_constant':1e-06} - -print ("#############TGV GPU####################") +pars = { + "algorithm": TGV, + "input": noisyVol, + "regularisation_parameter": 0.02, + "alpha1": 1.0, + "alpha0": 2.0, + "number_of_iterations": 500, + "LipshitzConstant": 12, + "tolerance_constant": 1e-06, +} + +print("#############TGV GPU####################") start_time = timeit.default_timer() -tgv_gpu3D = TGV(pars['input'], - pars['regularisation_parameter'], - pars['alpha1'], - pars['alpha0'], - pars['number_of_iterations'], - pars['LipshitzConstant'], - pars['tolerance_constant'], device='gpu', infovector=info_vec_gpu) +tgv_gpu3D = TGV( + pars["input"], + pars["regularisation_parameter"], + pars["alpha1"], + pars["alpha0"], + pars["number_of_iterations"], + pars["LipshitzConstant"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, tgv_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(tgv_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using TGV')) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________NDF-TV (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(tgv_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using TGV")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________NDF-TV (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of NDF regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of NDF regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : NDF, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.02, \ - 'edge_parameter':0.015,\ - 'number_of_iterations' :700 ,\ - 'time_marching_parameter':0.01,\ - 'penalty_type': 1,\ - 'tolerance_constant':1e-06} - - -print ("#############NDF GPU####################") +pars = { + "algorithm": NDF, + "input": noisyVol, + "regularisation_parameter": 0.02, + "edge_parameter": 0.015, + "number_of_iterations": 700, + "time_marching_parameter": 0.01, + "penalty_type": 1, + "tolerance_constant": 1e-06, +} + + +print("#############NDF GPU####################") start_time = timeit.default_timer() -ndf_gpu3D = NDF(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['penalty_type'], - pars['tolerance_constant'], device='gpu', infovector=info_vec_gpu) +ndf_gpu3D = NDF( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["penalty_type"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, ndf_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(ndf_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using NDF')) - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("___Anisotropic Diffusion 4th Order (3D)____") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(ndf_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using NDF")) + +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("___Anisotropic Diffusion 4th Order (3D)____") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of DIFF4th regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of DIFF4th regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : Diff4th, \ - 'input' : noisyVol,\ - 'regularisation_parameter':0.8, \ - 'edge_parameter':0.02,\ - 'number_of_iterations' :500 ,\ - 'time_marching_parameter':0.001,\ - 'tolerance_constant':1e-06} - -print ("#############DIFF4th GPU################") +pars = { + "algorithm": Diff4th, + "input": noisyVol, + "regularisation_parameter": 0.8, + "edge_parameter": 0.02, + "number_of_iterations": 500, + "time_marching_parameter": 0.001, + "tolerance_constant": 1e-06, +} + +print("#############DIFF4th GPU################") start_time = timeit.default_timer() -diff4_gpu3D = Diff4th(pars['input'], - pars['regularisation_parameter'], - pars['edge_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'],device='gpu', infovector=info_vec_gpu) +diff4_gpu3D = Diff4th( + pars["input"], + pars["regularisation_parameter"], + pars["edge_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + device="gpu", + infovector=info_vec_gpu, +) Qtools = QualityTools(idealVol, diff4_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(diff4_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('GPU results')) - -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________FGP-dTV (3D)________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") - -## plot +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(diff4_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("GPU results")) + +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________FGP-dTV (3D)________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") + +## plot fig = plt.figure() -plt.suptitle('Performance of FGP-dTV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy Image') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of FGP-dTV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy Image") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm' : FGP_dTV,\ - 'input' : noisyVol,\ - 'refdata' : noisyRef,\ - 'regularisation_parameter':0.02, - 'number_of_iterations' :500 ,\ - 'tolerance_constant':1e-06,\ - 'eta_const':0.2,\ - 'methodTV': 0 ,\ - 'nonneg': 0} - -print ("#############FGP TV GPU####################") +pars = { + "algorithm": FGP_dTV, + "input": noisyVol, + "refdata": noisyRef, + "regularisation_parameter": 0.02, + "number_of_iterations": 500, + "tolerance_constant": 1e-06, + "eta_const": 0.2, + "methodTV": 0, + "nonneg": 0, +} + +print("#############FGP TV GPU####################") start_time = timeit.default_timer() -fgp_dTV_gpu3D = FGP_dTV(pars['input'], - pars['refdata'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['eta_const'], - pars['methodTV'], - pars['nonneg'],device='gpu', infovector=info_vec_gpu) - +fgp_dTV_gpu3D = FGP_dTV( + pars["input"], + pars["refdata"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["tolerance_constant"], + pars["eta_const"], + pars["methodTV"], + pars["nonneg"], + device="gpu", + infovector=info_vec_gpu, +) + Qtools = QualityTools(idealVol, fgp_dTV_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(fgp_dTV_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using FGP-dTV')) -#%% +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(fgp_dTV_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using FGP-dTV")) +# %% diff --git a/demos/demo_gpu_regularisers3D_CuPy.py b/demos/demo_gpu_regularisers3D_CuPy.py index 2280672..af0715e 100644 --- a/demos/demo_gpu_regularisers3D_CuPy.py +++ b/demos/demo_gpu_regularisers3D_CuPy.py @@ -15,136 +15,154 @@ from ccpi.filters.regularisersCuPy import PD_TV as PD_TV_cupy from ccpi.supp.qualitymetrics import QualityTools + + ############################################################################### def printParametersToString(pars): - txt = r'' - for key, value in pars.items(): - if key== 'algorithm' : - txt += "{0} = {1}".format(key, value.__name__) - elif key == 'input': - txt += "{0} = {1}".format(key, np.shape(value)) - elif key == 'refdata': - txt += "{0} = {1}".format(key, np.shape(value)) - else: - txt += "{0} = {1}".format(key, value) - txt += '\n' - return txt + txt = r"" + for key, value in pars.items(): + if key == "algorithm": + txt += "{0} = {1}".format(key, value.__name__) + elif key == "input": + txt += "{0} = {1}".format(key, np.shape(value)) + elif key == "refdata": + txt += "{0} = {1}".format(key, np.shape(value)) + else: + txt += "{0} = {1}".format(key, value) + txt += "\n" + return txt + + ############################################################################### -filename = os.path.join( "../test/test_data" ,"peppers.tif") +filename = os.path.join("../test/test_data", "peppers.tif") # read image Im = imread(filename) -Im = Im/255.0 +Im = Im / 255.0 perc = 0.05 -u0 = Im + np.random.normal(loc = 0 , - scale = perc * Im , - size = np.shape(Im)) -u_ref = Im + np.random.normal(loc = 0 , - scale = 0.01 * Im , - size = np.shape(Im)) -(N,M) = np.shape(u0) -u0 = u0.astype('float32') -u_ref = u_ref.astype('float32') +u0 = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) +u_ref = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) +(N, M) = np.shape(u0) +u0 = u0.astype("float32") +u_ref = u_ref.astype("float32") slices = 20 -noisyVol = np.zeros((slices,N,M),dtype='float32') -noisyRef = np.zeros((slices,N,M),dtype='float32') -idealVol = np.zeros((slices,N,M),dtype='float32') +noisyVol = np.zeros((slices, N, M), dtype="float32") +noisyRef = np.zeros((slices, N, M), dtype="float32") +idealVol = np.zeros((slices, N, M), dtype="float32") + +for i in range(slices): + noisyVol[i, :, :] = Im + np.random.normal(loc=0, scale=perc * Im, size=np.shape(Im)) + noisyRef[i, :, :] = Im + np.random.normal(loc=0, scale=0.01 * Im, size=np.shape(Im)) + idealVol[i, :, :] = Im -for i in range (slices): - noisyVol[i,:,:] = Im + np.random.normal(loc = 0 , scale = perc * Im , size = np.shape(Im)) - noisyRef[i,:,:] = Im + np.random.normal(loc = 0 , scale = 0.01 * Im , size = np.shape(Im)) - idealVol[i,:,:] = Im - noisyVol = np.float32(noisyVol) -# move numpy array to CuPy. +# move numpy array to CuPy. # NOTE: Here we also need to be specific to which device we move the data to -gpu_device = 0 # select the device (if many) +gpu_device = 0 # select the device (if many) with cp.cuda.Device(gpu_device): noisyVol_cp = cp.asarray(noisyVol, order="C") -#%% +# %% # print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") # print ("_______________ROF-TV (3D)_________________") # print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +## plot fig = plt.figure() -plt.suptitle('Performance of ROF-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy 15th slice of the volume') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of ROF-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy 15th slice of the volume") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm': ROF_TV_cupy} +pars = {"algorithm": ROF_TV_cupy} start_time = timeit.default_timer() with cp.cuda.Device(gpu_device): - rof_gpu3D = ROF_TV_cupy(noisyVol_cp, - regularisation_parameter=0.02, - iterations = 4000, - time_marching_parameter=0.001, - gpu_id=gpu_device) + rof_gpu3D = ROF_TV_cupy( + noisyVol_cp, + regularisation_parameter=0.02, + iterations=4000, + time_marching_parameter=0.001, + gpu_id=gpu_device, + ) - rof_gpu3D = rof_gpu3D.get() # back to numpy + rof_gpu3D = rof_gpu3D.get() # back to numpy Qtools = QualityTools(idealVol, rof_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(rof_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using ROF-TV')) +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(rof_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using ROF-TV")) plt.savefig("rof_tv_image.png", dpi=250) -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________PD-TV (3D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -## plot +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________PD-TV (3D)_________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +## plot fig = plt.figure() -plt.suptitle('Performance of PD-TV regulariser using the GPU') -a=fig.add_subplot(1,2,1) -a.set_title('Noisy 15th slice of the volume') -imgplot = plt.imshow(noisyVol[10,:,:],cmap="gray") +plt.suptitle("Performance of PD-TV regulariser using the GPU") +a = fig.add_subplot(1, 2, 1) +a.set_title("Noisy 15th slice of the volume") +imgplot = plt.imshow(noisyVol[10, :, :], cmap="gray") # set parameters -pars = {'algorithm': PD_TV_cupy} +pars = {"algorithm": PD_TV_cupy} start_time = timeit.default_timer() with cp.cuda.Device(gpu_device): - pdtv_gpu3D = PD_TV_cupy(noisyVol_cp, - regularisation_parameter=0.06, - iterations = 2000, - methodTV=0, - nonneg=0, - lipschitz_const=8, - gpu_id=gpu_device) - - pdtv_gpu3D = pdtv_gpu3D.get() # back to numpy + pdtv_gpu3D = PD_TV_cupy( + noisyVol_cp, + regularisation_parameter=0.06, + iterations=2000, + methodTV=0, + nonneg=0, + lipschitz_const=8, + gpu_id=gpu_device, + ) + + pdtv_gpu3D = pdtv_gpu3D.get() # back to numpy Qtools = QualityTools(idealVol, pdtv_gpu3D) -pars['rmse'] = Qtools.rmse() +pars["rmse"] = Qtools.rmse() txtstr = printParametersToString(pars) -txtstr += "%s = %.3fs" % ('elapsed time',timeit.default_timer() - start_time) -print (txtstr) -a=fig.add_subplot(1,2,2) +txtstr += "%s = %.3fs" % ("elapsed time", timeit.default_timer() - start_time) +print(txtstr) +a = fig.add_subplot(1, 2, 2) # these are matplotlib.patch.Patch properties -props = dict(boxstyle='round', facecolor='wheat', alpha=0.75) +props = dict(boxstyle="round", facecolor="wheat", alpha=0.75) # place a text box in upper left in axes coords -a.text(0.15, 0.25, txtstr, transform=a.transAxes, fontsize=14, - verticalalignment='top', bbox=props) -imgplot = plt.imshow(pdtv_gpu3D[10,:,:], cmap="gray") -plt.title('{}'.format('Recovered volume on the GPU using PD-TV')) -plt.savefig("pd_tv_image.png", dpi=250) \ No newline at end of file +a.text( + 0.15, + 0.25, + txtstr, + transform=a.transAxes, + fontsize=14, + verticalalignment="top", + bbox=props, +) +imgplot = plt.imshow(pdtv_gpu3D[10, :, :], cmap="gray") +plt.title("{}".format("Recovered volume on the GPU using PD-TV")) +plt.savefig("pd_tv_image.png", dpi=250) diff --git a/demos/multi_gpu.py b/demos/multi_gpu.py index 357e38f..9bda297 100644 --- a/demos/multi_gpu.py +++ b/demos/multi_gpu.py @@ -12,62 +12,88 @@ @author: Daniil Kazantsev """ + def data_generator(): import numpy as np + vol_size = 160 - vol3d=np.float32(np.random.rand(vol_size,vol_size,vol_size)) + vol3d = np.float32(np.random.rand(vol_size, vol_size, vol_size)) return vol3d + def filter3D(vol3d, iterations_reg, DEVICE_no): from ccpi.filters.regularisers import ROF_TV - # perform basic data splitting between GPUs + + # perform basic data splitting between GPUs print("-----------------------------------------------------------------") print("Perform 3D filtering on {} GPU device...".format(DEVICE_no)) print("-----------------------------------------------------------------") # set parameters - pars = {'algorithm': ROF_TV, \ - 'input' : vol3d,\ - 'regularisation_parameter':0.01,\ - 'number_of_iterations': iterations_reg,\ - 'time_marching_parameter': 0.0001,\ - 'tolerance_constant': 0.0} - - (rof_gpu3D, info_vec_gpu) = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], DEVICE_no) + pars = { + "algorithm": ROF_TV, + "input": vol3d, + "regularisation_parameter": 0.01, + "number_of_iterations": iterations_reg, + "time_marching_parameter": 0.0001, + "tolerance_constant": 0.0, + } + + (rof_gpu3D, info_vec_gpu) = ROF_TV( + pars["input"], + pars["regularisation_parameter"], + pars["number_of_iterations"], + pars["time_marching_parameter"], + pars["tolerance_constant"], + DEVICE_no, + ) return rof_gpu3D -#%% + +# %% if __name__ == "__main__": - # imports + # imports from mpi4py import MPI - + # MPI process mpi_proc_num = MPI.COMM_WORLD.size - mpi_proc_id = MPI.COMM_WORLD.rank + mpi_proc_id = MPI.COMM_WORLD.rank # process arguments import argparse - parser = argparse.ArgumentParser (description="GPU device use from mpi4py") - parser.add_argument ("-g", "--get_device", action="store_true", help="report device for each MPI process (default: NO)") - parser.add_argument ("-s", "--set_device", action="store_true", help="automatically set device for each MPI process (default: NO)") - parser.add_argument('-gpus', '--gpus_no', dest='gpus_total', default=2, help='the total number of available GPU devices') - args = parser.parse_args () + + parser = argparse.ArgumentParser(description="GPU device use from mpi4py") + parser.add_argument( + "-g", + "--get_device", + action="store_true", + help="report device for each MPI process (default: NO)", + ) + parser.add_argument( + "-s", + "--set_device", + action="store_true", + help="automatically set device for each MPI process (default: NO)", + ) + parser.add_argument( + "-gpus", + "--gpus_no", + dest="gpus_total", + default=2, + help="the total number of available GPU devices", + ) + args = parser.parse_args() # Generating the projection data # NOTE that the data is generated for each mpi process for the sake of simplicity but it could be splitted # into multiple mpi processess generating smaller chunks of the global dataset vol3d = data_generator() - + # set the total number of available GPU devices GPUs_total_num = int(args.gpus_total) DEVICE_no = mpi_proc_id % GPUs_total_num - - # perform filtering: - iterations_reg=3000 - filtered3D = filter3D(vol3d, iterations_reg, DEVICE_no) -#%% + # perform filtering: + iterations_reg = 3000 + filtered3D = filter3D(vol3d, iterations_reg, DEVICE_no) +# %% diff --git a/src/Python/ccpi/filters/TV.py b/src/Python/ccpi/filters/TV.py index 1f8aef4..363a4d3 100644 --- a/src/Python/ccpi/filters/TV.py +++ b/src/Python/ccpi/filters/TV.py @@ -3,26 +3,35 @@ from .utils import cilreg, cilregcuda -#%% - -def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, - marching_step_parameter, tolerance_param, out=None, infovector=None): +# %% + + +def TV_ROF_CPU( + inputData, + regularisation_parameter, + iterationsNumb, + marching_step_parameter, + tolerance_param, + out=None, + infovector=None, +): # float TV_ROF_CPU_main(float *Input, float *Output, float *infovector, -# float *lambdaPar, int lambda_is_arr, int iterationsNumb, float tau, -# float epsil, int dimX, int dimY, int dimZ); - cilreg.TV_ROF_CPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the input array - ctypes.POINTER(ctypes.c_float), # pointer to the output array - ctypes.POINTER(ctypes.c_float), # pointer to the infoVector array - ctypes.POINTER(ctypes.c_float), # type of type of lambdaPar (float) - ctypes.c_int, # lambda_is_arr (int) - ctypes.c_int, # type of type of iterationsNumb (int) - ctypes.c_float, # type of type of tau (float) - ctypes.c_float, # type of type of epsil (float) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + # float *lambdaPar, int lambda_is_arr, int iterationsNumb, float tau, + # float epsil, int dimX, int dimY, int dimZ); + cilreg.TV_ROF_CPU_main.argtypes = [ + ctypes.POINTER(ctypes.c_float), # pointer to the input array + ctypes.POINTER(ctypes.c_float), # pointer to the output array + ctypes.POINTER(ctypes.c_float), # pointer to the infoVector array + ctypes.POINTER(ctypes.c_float), # type of type of lambdaPar (float) + ctypes.c_int, # lambda_is_arr (int) + ctypes.c_int, # type of type of iterationsNumb (int) + ctypes.c_float, # type of type of tau (float) + ctypes.c_float, # type of type of epsil (float) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.TV_ROF_CPU_main.restype = ctypes.c_float # return value is float + cilreg.TV_ROF_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -31,7 +40,7 @@ def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -39,35 +48,52 @@ def TV_ROF_CPU(inputData, regularisation_parameter, iterationsNumb, dims.append(1) # float TV_ROF_CPU_main(float *Input, float *Output, float *infovector, -# float *lambdaPar, int lambda_is_arr, int iterationsNumb, float tau, -# float epsil, int dimX, int dimY, int dimZ); - cilreg.TV_ROF_CPU_main(in_p, out_p, infovector_p, - ctypes.byref(ctypes.c_float(regularisation_parameter)), - 0, - iterationsNumb, - marching_step_parameter, tolerance_param, - dims[0], dims[1], dims[2]) + # float *lambdaPar, int lambda_is_arr, int iterationsNumb, float tau, + # float epsil, int dimX, int dimY, int dimZ); + cilreg.TV_ROF_CPU_main( + in_p, + out_p, + infovector_p, + ctypes.byref(ctypes.c_float(regularisation_parameter)), + 0, + iterationsNumb, + marching_step_parameter, + tolerance_param, + dims[0], + dims[1], + dims[2], + ) return out -#%% From copilot chat -def TV_FGP_CPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, nonneg, out=None, infovector=None): + +# %% From copilot chat +def TV_FGP_CPU( + inputData, + lambdaPar, + iterationsNumb, + epsil, + methodTV, + nonneg, + out=None, + infovector=None, +): # float TV_FGP_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # int iterationsNumb, float epsil, int methodTV, int nonneg, int dimX, int dimY, int dimZ); cilreg.TV_FGP_CPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # epsil (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # nonneg (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # epsil (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # nonneg (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.TV_FGP_CPU_main.restype = ctypes.c_float # return value is float + cilreg.TV_FGP_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -76,7 +102,7 @@ def TV_FGP_CPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, nonneg, ou out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -85,12 +111,34 @@ def TV_FGP_CPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, nonneg, ou # float TV_FGP_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # int iterationsNumb, float epsil, int methodTV, int nonneg, int dimX, int dimY, int dimZ); - cilreg.TV_FGP_CPU_main(in_p, out_p, infovector_p, lambdaPar, iterationsNumb, epsil, methodTV, nonneg, dims[0], dims[1], dims[2]) + cilreg.TV_FGP_CPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + iterationsNumb, + epsil, + methodTV, + nonneg, + dims[0], + dims[1], + dims[2], + ) return out -def PDTV_CPU(inputData, lambdaPar, iterationsNumb, epsil, lipschitz_const, methodTV, - nonneg, out=None, infovector=None): + +def PDTV_CPU( + inputData, + lambdaPar, + iterationsNumb, + epsil, + lipschitz_const, + methodTV, + nonneg, + out=None, + infovector=None, +): # float PDTV_CPU_main(float *Input, float *U, float *infovector, # float lambdaPar, int iterationsNumb, float epsil, # float lipschitz_const, int methodTV, int nonneg, @@ -100,17 +148,17 @@ def PDTV_CPU(inputData, lambdaPar, iterationsNumb, epsil, lipschitz_const, metho ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the U array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # epsil (float) - ctypes.c_float, # lipschitz_const (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # nonneg (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # epsil (float) + ctypes.c_float, # lipschitz_const (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # nonneg (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.PDTV_CPU_main.restype = ctypes.c_float # return value is float + cilreg.PDTV_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -119,7 +167,7 @@ def PDTV_CPU(inputData, lambdaPar, iterationsNumb, epsil, lipschitz_const, metho out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -129,11 +177,24 @@ def PDTV_CPU(inputData, lambdaPar, iterationsNumb, epsil, lipschitz_const, metho # float PDTV_CPU_main(float *Input, float *U, float *infovector, float lambdaPar, # int iterationsNumb, float epsil, float lipschitz_const, int methodTV, int nonneg, # int dimX, int dimY, int dimZ); - cilreg.PDTV_CPU_main(in_p, out_p, infovector_p, lambdaPar, iterationsNumb, - epsil, lipschitz_const, methodTV, nonneg, dims[0], dims[1], dims[2]) + cilreg.PDTV_CPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + iterationsNumb, + epsil, + lipschitz_const, + methodTV, + nonneg, + dims[0], + dims[1], + dims[2], + ) return out + def SB_TV_CPU(inputData, mu, iter, epsil, methodTV, out=None, infovector=None): # float SB_TV_CPU_main(float *Input, float *Output, float *infovector, float mu, # int iter, float epsil, int methodTV, int dimX, int dimY, int dimZ); @@ -141,15 +202,15 @@ def SB_TV_CPU(inputData, mu, iter, epsil, methodTV, out=None, infovector=None): ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # mu (float) - ctypes.c_int, # iter (int) - ctypes.c_float, # epsil (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # mu (float) + ctypes.c_int, # iter (int) + ctypes.c_float, # epsil (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.SB_TV_CPU_main.restype = ctypes.c_float # return value is float + cilreg.SB_TV_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -158,7 +219,7 @@ def SB_TV_CPU(inputData, mu, iter, epsil, methodTV, out=None, infovector=None): out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -167,27 +228,39 @@ def SB_TV_CPU(inputData, mu, iter, epsil, methodTV, out=None, infovector=None): # float SB_TV_CPU_main(float *Input, float *Output, float *infovector, float mu, # int iter, float epsil, int methodTV, int dimX, int dimY, int dimZ); - cilreg.SB_TV_CPU_main(in_p, out_p, infovector_p, mu, iter, epsil, methodTV, dims[0], dims[1], dims[2]) + cilreg.SB_TV_CPU_main( + in_p, out_p, infovector_p, mu, iter, epsil, methodTV, dims[0], dims[1], dims[2] + ) return out -def LLT_ROF_CPU(inputData, lambdaROF, lambdaLLT, iterationsNumb, tau, epsil, out=None, infovector=None): + +def LLT_ROF_CPU( + inputData, + lambdaROF, + lambdaLLT, + iterationsNumb, + tau, + epsil, + out=None, + infovector=None, +): # float LLT_ROF_CPU_main(float *Input, float *Output, float *infovector, float lambdaROF, # float lambdaLLT, int iterationsNumb, float tau, float epsil, int dimX, int dimY, int dimZ); cilreg.LLT_ROF_CPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaROF (float) - ctypes.c_float, # lambdaLLT (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # tau (float) - ctypes.c_float, # epsil (float) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaROF (float) + ctypes.c_float, # lambdaLLT (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # tau (float) + ctypes.c_float, # epsil (float) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.LLT_ROF_CPU_main.restype = ctypes.c_float # return value is float + cilreg.LLT_ROF_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -196,7 +269,7 @@ def LLT_ROF_CPU(inputData, lambdaROF, lambdaLLT, iterationsNumb, tau, epsil, out out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -205,29 +278,52 @@ def LLT_ROF_CPU(inputData, lambdaROF, lambdaLLT, iterationsNumb, tau, epsil, out # float LLT_ROF_CPU_main(float *Input, float *Output, float *infovector, float lambdaROF, # float lambdaLLT, int iterationsNumb, float tau, float epsil, int dimX, int dimY, int dimZ); - cilreg.LLT_ROF_CPU_main(in_p, out_p, infovector_p, lambdaROF, lambdaLLT, iterationsNumb, tau, epsil, dims[0], dims[1], dims[2]) + cilreg.LLT_ROF_CPU_main( + in_p, + out_p, + infovector_p, + lambdaROF, + lambdaLLT, + iterationsNumb, + tau, + epsil, + dims[0], + dims[1], + dims[2], + ) return out + # TGV -def TGV_CPU(inputData, lambdaPar, alpha1, alpha0, iterationsNumb, L2, epsil, out=None, infovector=None): +def TGV_CPU( + inputData, + lambdaPar, + alpha1, + alpha0, + iterationsNumb, + L2, + epsil, + out=None, + infovector=None, +): # float TGV_main(float *Input, float *Output, float *infovector, float lambdaPar, # float alpha1, float alpha0, int iterationsNumb, float L2, float epsil, int dimX, int dimY, int dimZ); cilreg.TGV_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_float, # alpha1 (float) - ctypes.c_float, # alpha0 (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # L2 (float) - ctypes.c_float, # epsil (float) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_float, # alpha1 (float) + ctypes.c_float, # alpha0 (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # L2 (float) + ctypes.c_float, # epsil (float) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.TGV_main.restype = ctypes.c_float # return value is float + cilreg.TGV_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -236,7 +332,7 @@ def TGV_CPU(inputData, lambdaPar, alpha1, alpha0, iterationsNumb, L2, epsil, out out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -245,12 +341,37 @@ def TGV_CPU(inputData, lambdaPar, alpha1, alpha0, iterationsNumb, L2, epsil, out # float TGV_main(float *Input, float *Output, float *infovector, float lambdaPar, # float alpha1, float alpha0, int iterationsNumb, float L2, float epsil, int dimX, int dimY, int dimZ); - cilreg.TGV_main(in_p, out_p, infovector_p, lambdaPar, alpha1, alpha0, iterationsNumb, L2, epsil, dims[0], dims[1], dims[2]) + cilreg.TGV_main( + in_p, + out_p, + infovector_p, + lambdaPar, + alpha1, + alpha0, + iterationsNumb, + L2, + epsil, + dims[0], + dims[1], + dims[2], + ) return out + # dTV -def dTV_FGP_CPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, methodTV, nonneg, out=None, infovector=None): +def dTV_FGP_CPU( + inputData, + inputRef, + lambdaPar, + iterationsNumb, + epsil, + eta, + methodTV, + nonneg, + out=None, + infovector=None, +): # dTV_FGP_CPU_main(float *Input, float *InputRef, float *Output, float *infovector, float lambdaPar, # int iterationsNumb, float epsil, float eta, int methodTV, int nonneg, int dimX, int dimY, int dimZ); cilreg.dTV_FGP_CPU_main.argtypes = [ @@ -258,17 +379,17 @@ def dTV_FGP_CPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, meth ctypes.POINTER(ctypes.c_float), # pointer to the InputRef array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # epsil (float) - ctypes.c_float, # eta (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # nonneg (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # epsil (float) + ctypes.c_float, # eta (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # nonneg (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.dTV_FGP_CPU_main.restype = ctypes.c_float # return value is float + cilreg.dTV_FGP_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) inref_p = inputRef.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -278,7 +399,7 @@ def dTV_FGP_CPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, meth out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -287,24 +408,39 @@ def dTV_FGP_CPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, meth # dTV_FGP_CPU_main(float *Input, float *InputRef, float *Output, float *infovector, float lambdaPar, # int iterationsNumb, float epsil, float eta, int methodTV, int nonneg, int dimX, int dimY, int dimZ); - cilreg.dTV_FGP_CPU_main(in_p, inref_p, out_p, infovector_p, lambdaPar, iterationsNumb, epsil, eta, methodTV, nonneg, dims[0], dims[1], dims[2]) + cilreg.dTV_FGP_CPU_main( + in_p, + inref_p, + out_p, + infovector_p, + lambdaPar, + iterationsNumb, + epsil, + eta, + methodTV, + nonneg, + dims[0], + dims[1], + dims[2], + ) return out -#TNV + +# TNV def TNV(inputData, lambdaPar, maxIter, tol, out=None): # float TNV_CPU_main(float *Input, float *u, float lambdaPar, int maxIter, float tol, int dimX, int dimY, int dimZ); cilreg.TNV_CPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the u array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # maxIter (int) - ctypes.c_float, # tol (float) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # maxIter (int) + ctypes.c_float, # tol (float) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.TNV_CPU_main.restype = ctypes.c_float # return value is float + cilreg.TNV_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -314,47 +450,57 @@ def TNV(inputData, lambdaPar, maxIter, tol, out=None): dims = list(inputData.shape)[::-1] if inputData.ndim != 3: - raise ValueError('Input data must be 2D + 1 channel') + raise ValueError("Input data must be 2D + 1 channel") # float TNV_CPU_main(float *Input, float *u, float lambdaPar, int maxIter, float tol, int dimX, int dimY, int dimZ); cilreg.TNV_CPU_main(in_p, out_p, lambdaPar, maxIter, tol, dims[0], dims[1], dims[2]) return out + # Non-local TV # usage example https://github.com/TomographicImaging/CCPi-Regularisation-Toolkit/blob/71f8d304d804b54d378f0ed05539f01aaaf13758/demos/demo_gpu_regularisers.py#L438-L506 -def PatchSelect_CPU(inputData, SearchWindow, SimilarWin, NumNeighb, h, H_i=None, H_j=None, H_k=None, Weights=None): +def PatchSelect_CPU( + inputData, + SearchWindow, + SimilarWin, + NumNeighb, + h, + H_i=None, + H_j=None, + H_k=None, + Weights=None, +): # float PatchSelect_CPU_main(float *Input, unsigned short *H_i, unsigned short *H_j, unsigned short *H_k, # float *Weights, int dimX, int dimY, int dimZ, int SearchWindow, int SimilarWin, int NumNeighb, float h); cilreg.PatchSelect_CPU_main.argtypes = [ - ctypes.POINTER(ctypes.c_float), # pointer to the Input array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_i array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_j array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_k array - ctypes.POINTER(ctypes.c_float), # pointer to the Weights array - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) - ctypes.c_int, # SearchWindow (int) - ctypes.c_int, # SimilarWin (int) - ctypes.c_int, # NumNeighb (int) - ctypes.c_float, # h (float) + ctypes.POINTER(ctypes.c_float), # pointer to the Input array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_i array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_j array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_k array + ctypes.POINTER(ctypes.c_float), # pointer to the Weights array + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) + ctypes.c_int, # SearchWindow (int) + ctypes.c_int, # SimilarWin (int) + ctypes.c_int, # NumNeighb (int) + ctypes.c_float, # h (float) ] - cilreg.PatchSelect_CPU_main.restype = ctypes.c_float # return value is float + cilreg.PatchSelect_CPU_main.restype = ctypes.c_float # return value is float if inputData.ndim != 2: # See https://github.com/TomographicImaging/CCPi-Regularisation-Toolkit/issues/184 - raise ValueError('PatchSelect_CPU: Only 2D images are supported') + raise ValueError("PatchSelect_CPU: Only 2D images are supported") dims = [NumNeighb, inputData.shape[0], inputData.shape[1]] if Weights is None: - Weights = np.zeros(dims, dtype='float32') + Weights = np.zeros(dims, dtype="float32") if H_i is None: - H_i = np.zeros(dims, dtype='uint16') + H_i = np.zeros(dims, dtype="uint16") if H_j is None: - H_j = np.zeros(dims, dtype='uint16') + H_j = np.zeros(dims, dtype="uint16") if H_k is None: - H_k = np.zeros(dims, dtype='uint16') - + H_k = np.zeros(dims, dtype="uint16") in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) hi_p = H_i.ctypes.data_as(ctypes.POINTER(ctypes.c_ushort)) @@ -364,12 +510,36 @@ def PatchSelect_CPU(inputData, SearchWindow, SimilarWin, NumNeighb, h, H_i=None, # float PatchSelect_CPU_main(float *Input, unsigned short *H_i, unsigned short *H_j, unsigned short *H_k, # float *Weights, int dimX, int dimY, int dimZ, int SearchWindow, int SimilarWin, int NumNeighb, float h); - cilreg.PatchSelect_CPU_main(in_p, hi_p, hj_p, hk_p, weights_p, dims[2], dims[1], 0, SearchWindow, SimilarWin, NumNeighb, h) + cilreg.PatchSelect_CPU_main( + in_p, + hi_p, + hj_p, + hk_p, + weights_p, + dims[2], + dims[1], + 0, + SearchWindow, + SimilarWin, + NumNeighb, + h, + ) return H_i, H_j, Weights -def NLTV(inputData, H_i, H_j, H_k, Weights, NumNeighb, lambdaReg, IterNumb, - switchM=1, Output=None): + +def NLTV( + inputData, + H_i, + H_j, + H_k, + Weights, + NumNeighb, + lambdaReg, + IterNumb, + switchM=1, + Output=None, +): # switchM=1 is the default value # H_k is not used as only 2D and it is passed as H_i to the C function # see below link for the original code @@ -378,25 +548,27 @@ def NLTV(inputData, H_i, H_j, H_k, Weights, NumNeighb, lambdaReg, IterNumb, # float Nonlocal_TV_CPU_main(float *A_orig, float *Output, unsigned short *H_i, unsigned short *H_j, unsigned short *H_k, # float *Weights, int dimX, int dimY, int dimZ, int NumNeighb, float lambdaReg, int IterNumb, int switchM); cilreg.Nonlocal_TV_CPU_main.argtypes = [ - ctypes.POINTER(ctypes.c_float), # pointer to the A_orig array - ctypes.POINTER(ctypes.c_float), # pointer to the Output array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_i array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_j array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_k array - ctypes.POINTER(ctypes.c_float), # pointer to the Weights array - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) - ctypes.c_int, # NumNeighb (int) - ctypes.c_float, # lambdaReg (float) - ctypes.c_int, # IterNumb (int) - ctypes.c_int, # switchM (int) + ctypes.POINTER(ctypes.c_float), # pointer to the A_orig array + ctypes.POINTER(ctypes.c_float), # pointer to the Output array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_i array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_j array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_k array + ctypes.POINTER(ctypes.c_float), # pointer to the Weights array + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) + ctypes.c_int, # NumNeighb (int) + ctypes.c_float, # lambdaReg (float) + ctypes.c_int, # IterNumb (int) + ctypes.c_int, # switchM (int) ] - cilreg.Nonlocal_TV_CPU_main.restype = ctypes.c_float # return value is float + cilreg.Nonlocal_TV_CPU_main.restype = ctypes.c_float # return value is float dims = list(inputData.shape) if inputData.ndim != 2: - raise ValueError(f'NLTV can only process 2D data. Got {inputData.ndim} dimensions') + raise ValueError( + f"NLTV can only process 2D data. Got {inputData.ndim} dimensions" + ) aorig_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) hi_p = H_i.ctypes.data_as(ctypes.POINTER(ctypes.c_ushort)) @@ -410,19 +582,32 @@ def NLTV(inputData, H_i, H_j, H_k, Weights, NumNeighb, lambdaReg, IterNumb, Output = np.zeros_like(inputData) out_p = Output.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - - # float Nonlocal_TV_CPU_main(float *A_orig, float *Output, unsigned short *H_i, unsigned short *H_j, unsigned short *H_k, # float *Weights, int dimX, int dimY, int dimZ, int NumNeighb, float lambdaReg, int IterNumb, int switchM); - result = cilreg.Nonlocal_TV_CPU_main(aorig_p, out_p, hi_p, hj_p, hk_p, weights_p, dims[1], dims[0], 0, NumNeighb, lambdaReg, IterNumb, switchM) + result = cilreg.Nonlocal_TV_CPU_main( + aorig_p, + out_p, + hi_p, + hj_p, + hk_p, + weights_p, + dims[1], + dims[0], + 0, + NumNeighb, + lambdaReg, + IterNumb, + switchM, + ) return Output + def TV_ENERGY(U, U0, lambdaPar, type, E_val=None): u_p = U.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) u0_p = U0.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - E_val = np.zeros([1], dtype='float32') + E_val = np.zeros([1], dtype="float32") e_val_p = E_val.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(U.shape) @@ -433,29 +618,33 @@ def TV_ENERGY(U, U0, lambdaPar, type, E_val=None): ctypes.POINTER(ctypes.c_float), # pointer to the U array ctypes.POINTER(ctypes.c_float), # pointer to the U0 array ctypes.POINTER(ctypes.c_float), # pointer to the E_val array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # type (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # type (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) ] - cilreg.TV_energy2D.restype = ctypes.c_float # return value is float - result = cilreg.TV_energy2D(u_p, u0_p, e_val_p, lambdaPar, type, dims[1], dims[0]) + cilreg.TV_energy2D.restype = ctypes.c_float # return value is float + result = cilreg.TV_energy2D( + u_p, u0_p, e_val_p, lambdaPar, type, dims[1], dims[0] + ) elif U.ndim == 3: # float TV_energy3D(float *U, float *U0, float *E_val, float lambdaPar, int type, int dimX, int dimY, int dimZ); cilreg.TV_energy3D.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the U array ctypes.POINTER(ctypes.c_float), # pointer to the U0 array ctypes.POINTER(ctypes.c_float), # pointer to the E_val array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # type (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # type (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.TV_energy3D.restype = ctypes.c_float # return value is float + cilreg.TV_energy3D.restype = ctypes.c_float # return value is float # float TV_energy3D(float *U, float *U0, float *E_val, float lambdaPar, int type, int dimX, int dimY, int dimZ); - result = cilreg.TV_energy3D(u_p, u0_p, e_val_p, lambdaPar, type, dims[2], dims[1], dims[0]) + result = cilreg.TV_energy3D( + u_p, u0_p, e_val_p, lambdaPar, type, dims[2], dims[1], dims[0] + ) else: raise ValueError(f"TV_ENERGY: Only 2D and 3D data are supported. Got {U.ndim}") return E_val @@ -474,24 +663,34 @@ def TV_ENERGY(U, U0, lambdaPar, type, E_val=None): PatchSelect_GPU = None if cilregcuda is not None: - def TV_ROF_GPU(inputData, regularisation_parameter, iterationsNumb, - marching_step_parameter, tolerance_param, gpu_device, out=None, infovector=None): + + def TV_ROF_GPU( + inputData, + regularisation_parameter, + iterationsNumb, + marching_step_parameter, + tolerance_param, + gpu_device, + out=None, + infovector=None, + ): # int TV_ROF_GPU_main(float* Input, float* Output, float *infovector, # float lambdaPar, int iter, float tau, float epsil, int gpu_device, # int N, int M, int Z); - cilregcuda.TV_ROF_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the input array - ctypes.POINTER(ctypes.c_float), # pointer to the output array - ctypes.POINTER(ctypes.c_float), # pointer to the infoVector array - ctypes.c_float, # type of type of lambdaPar (float) - ctypes.c_int, # type of type of iterationsNumb (int) - ctypes.c_float, # type of type of tau (float) - ctypes.c_float, # type of type of epsil (float) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # Z (int) + cilregcuda.TV_ROF_GPU_main.argtypes = [ + ctypes.POINTER(ctypes.c_float), # pointer to the input array + ctypes.POINTER(ctypes.c_float), # pointer to the output array + ctypes.POINTER(ctypes.c_float), # pointer to the infoVector array + ctypes.c_float, # type of type of lambdaPar (float) + ctypes.c_int, # type of type of iterationsNumb (int) + ctypes.c_float, # type of type of tau (float) + ctypes.c_float, # type of type of epsil (float) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # Z (int) ] - cilreg.TV_ROF_CPU_main.restype = ctypes.c_float # return value is float + cilreg.TV_ROF_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -500,7 +699,7 @@ def TV_ROF_GPU(inputData, regularisation_parameter, iterationsNumb, out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -508,38 +707,54 @@ def TV_ROF_GPU(inputData, regularisation_parameter, iterationsNumb, dims.append(1) # float TV_ROF_CPU_main(float *Input, float *Output, float *infovector, - # float *lambdaPar, int lambda_is_arr, int iterationsNumb, float tau, - # float epsil, int dimX, int dimY, int dimZ); - if gpu_device == 'gpu': + # float *lambdaPar, int lambda_is_arr, int iterationsNumb, float tau, + # float epsil, int dimX, int dimY, int dimZ); + if gpu_device == "gpu": gpu_device = 0 - cilregcuda.TV_ROF_GPU_main(in_p, out_p, infovector_p, - regularisation_parameter, - iterationsNumb, - marching_step_parameter, tolerance_param, - gpu_device, - dims[0], dims[1], dims[2]) + cilregcuda.TV_ROF_GPU_main( + in_p, + out_p, + infovector_p, + regularisation_parameter, + iterationsNumb, + marching_step_parameter, + tolerance_param, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out - def TV_FGP_GPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, nonneg, gpu_device, - out=None, infovector=None): + def TV_FGP_GPU( + inputData, + lambdaPar, + iterationsNumb, + epsil, + methodTV, + nonneg, + gpu_device, + out=None, + infovector=None, + ): # int TV_FGP_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # int iter, float epsil, int methodTV, int nonneg, int gpu_device, int N, int M, int Z); cilregcuda.TV_FGP_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # iter (int) - ctypes.c_float, # epsil (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # nonneg (int) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # Z (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # iter (int) + ctypes.c_float, # epsil (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # nonneg (int) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # Z (int) ] - cilregcuda.TV_FGP_GPU_main.restype = ctypes.c_int # return value is float + cilregcuda.TV_FGP_GPU_main.restype = ctypes.c_int # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -548,41 +763,63 @@ def TV_FGP_GPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, nonneg, gp out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] if inputData.ndim == 2: dims.append(1) - if gpu_device == 'gpu': + if gpu_device == "gpu": gpu_device = 0 # TV_FGP_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # int iter, float epsil, int methodTV, int nonneg, int gpu_device, int N, int M, int Z); - cilregcuda.TV_FGP_GPU_main(in_p, out_p, infovector_p, lambdaPar, iterationsNumb, epsil, methodTV, - nonneg, - gpu_device, dims[0], dims[1], dims[2]) + cilregcuda.TV_FGP_GPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + iterationsNumb, + epsil, + methodTV, + nonneg, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out - def PDTV_GPU(Input, lambdaPar, iter, epsil, lipschitz_const, methodTV, nonneg, gpu_device, Output=None, infovector=None): + def PDTV_GPU( + Input, + lambdaPar, + iter, + epsil, + lipschitz_const, + methodTV, + nonneg, + gpu_device, + Output=None, + infovector=None, + ): # int TV_PD_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, int iter, float epsil, # float lipschitz_const, int methodTV, int nonneg, int gpu_device, int dimX, int dimY, int dimZ); cilregcuda.TV_PD_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # iter (int) - ctypes.c_float, # epsil (float) - ctypes.c_float, # lipschitz_const (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # nonneg (int) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # iter (int) + ctypes.c_float, # epsil (float) + ctypes.c_float, # lipschitz_const (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # nonneg (int) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilregcuda.TV_PD_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.TV_PD_GPU_main.restype = ctypes.c_int # return value is int input_p = Input.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -599,31 +836,50 @@ def PDTV_GPU(Input, lambdaPar, iter, epsil, lipschitz_const, methodTV, nonneg, g dims.append(1) # int TV_PD_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, int iter, float epsil, # float lipschitz_const, int methodTV, int nonneg, int gpu_device, int dimX, int dimY, int dimZ); - result = cilregcuda.TV_PD_GPU_main(input_p, output_p, infovector_p, - lambdaPar, iter, epsil, lipschitz_const, - methodTV, nonneg, gpu_device, - dims[0], dims[1], dims[2]) + result = cilregcuda.TV_PD_GPU_main( + input_p, + output_p, + infovector_p, + lambdaPar, + iter, + epsil, + lipschitz_const, + methodTV, + nonneg, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return Output - def SB_TV_GPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, - gpu_device, out=None, infovector=None): + def SB_TV_GPU( + inputData, + lambdaPar, + iterationsNumb, + epsil, + methodTV, + gpu_device, + out=None, + infovector=None, + ): # int TV_SB_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # int iter, float epsil, int methodTV, int gpu_device, int N, int M, int Z); cilregcuda.TV_SB_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # iter (int) - ctypes.c_float, # epsil (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # Z (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # iter (int) + ctypes.c_float, # epsil (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # Z (int) ] - cilregcuda.TV_SB_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.TV_SB_GPU_main.restype = ctypes.c_int # return value is int in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -632,7 +888,7 @@ def SB_TV_GPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -641,30 +897,50 @@ def SB_TV_GPU(inputData, lambdaPar, iterationsNumb, epsil, methodTV, # int TV_SB_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, int iter, float epsil, # int methodTV, int gpu_device, int N, int M, int Z); - result = cilregcuda.TV_SB_GPU_main(in_p, out_p, infovector_p, lambdaPar, - iterationsNumb, epsil, methodTV, - gpu_device, dims[0], dims[1], dims[2]) + result = cilregcuda.TV_SB_GPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + iterationsNumb, + epsil, + methodTV, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out - def LLT_ROF_GPU(inputData, lambdaROF, lambdaLLT, iterationsNumb, tau, epsil, gpu_device, out=None, infovector=None): + def LLT_ROF_GPU( + inputData, + lambdaROF, + lambdaLLT, + iterationsNumb, + tau, + epsil, + gpu_device, + out=None, + infovector=None, + ): # int LLT_ROF_GPU_main(float *Input, float *Output, float *infovector, float lambdaROF, float lambdaLLT, # int iterationsNumb, float tau, float epsil, int gpu_device, int N, int M, int Z); cilregcuda.LLT_ROF_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaROF (float) - ctypes.c_float, # lambdaLLT (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # tau (float) - ctypes.c_float, # epsil (float) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # Z (int) + ctypes.c_float, # lambdaROF (float) + ctypes.c_float, # lambdaLLT (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # tau (float) + ctypes.c_float, # epsil (float) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # Z (int) ] - cilregcuda.LLT_ROF_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.LLT_ROF_GPU_main.restype = ctypes.c_int # return value is int in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -673,7 +949,7 @@ def LLT_ROF_GPU(inputData, lambdaROF, lambdaLLT, iterationsNumb, tau, epsil, gpu out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -682,32 +958,53 @@ def LLT_ROF_GPU(inputData, lambdaROF, lambdaLLT, iterationsNumb, tau, epsil, gpu # int LLT_ROF_GPU_main(float *Input, float *Output, float *infovector, float lambdaROF, float lambdaLLT, # int iterationsNumb, float tau, float epsil, int gpu_device, int N, int M, int Z); - result = cilregcuda.LLT_ROF_GPU_main(in_p, out_p, infovector_p, - lambdaROF, lambdaLLT, iterationsNumb, tau, - epsil, gpu_device, dims[0], dims[1], dims[2]) + result = cilregcuda.LLT_ROF_GPU_main( + in_p, + out_p, + infovector_p, + lambdaROF, + lambdaLLT, + iterationsNumb, + tau, + epsil, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out - def TGV_GPU(inputData, lambdaPar, alpha1, alpha0, iterationsNumb, L2, epsil, - gpu_device, out=None, infovector=None): + def TGV_GPU( + inputData, + lambdaPar, + alpha1, + alpha0, + iterationsNumb, + L2, + epsil, + gpu_device, + out=None, + infovector=None, + ): # int TGV_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float alpha1, float alpha0, # int iterationsNumb, float L2, float epsil, int gpu_device, int dimX, int dimY, int dimZ); cilregcuda.TGV_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_float, # alpha1 (float) - ctypes.c_float, # alpha0 (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # L2 (float) - ctypes.c_float, # epsil (float) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_float, # alpha1 (float) + ctypes.c_float, # alpha0 (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # L2 (float) + ctypes.c_float, # epsil (float) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilregcuda.TGV_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.TGV_GPU_main.restype = ctypes.c_int # return value is int in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -716,7 +1013,7 @@ def TGV_GPU(inputData, lambdaPar, alpha1, alpha0, iterationsNumb, L2, epsil, out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -726,14 +1023,37 @@ def TGV_GPU(inputData, lambdaPar, alpha1, alpha0, iterationsNumb, L2, epsil, # int TGV_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float alpha1, float alpha0, # int iterationsNumb, float L2, float epsil, int gpu_device, int dimX, int dimY, int dimZ); - result = cilregcuda.TGV_GPU_main(in_p, out_p, infovector_p, lambdaPar, - alpha1, alpha0, iterationsNumb, L2, - epsil, gpu_device, dims[0], dims[1], dims[2]) + result = cilregcuda.TGV_GPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + alpha1, + alpha0, + iterationsNumb, + L2, + epsil, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out - def dTV_FGP_GPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, - methodTV, nonneg, gpu_device, out=None, infovector=None): + def dTV_FGP_GPU( + inputData, + inputRef, + lambdaPar, + iterationsNumb, + epsil, + eta, + methodTV, + nonneg, + gpu_device, + out=None, + infovector=None, + ): # int dTV_FGP_GPU_main(float *Input, float *InputRef, float *Output, float *infovector, float lambdaPar, # int iterationsNumb, float epsil, float eta, int methodTV, int nonneg, int gpu_device, int N, int M, int Z); cilregcuda.dTV_FGP_GPU_main.argtypes = [ @@ -741,18 +1061,18 @@ def dTV_FGP_GPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, ctypes.POINTER(ctypes.c_float), # pointer to the InputRef array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # epsil (float) - ctypes.c_float, # eta (float) - ctypes.c_int, # methodTV (int) - ctypes.c_int, # nonneg (int) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # Z (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # epsil (float) + ctypes.c_float, # eta (float) + ctypes.c_int, # methodTV (int) + ctypes.c_int, # nonneg (int) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # Z (int) ] - cilregcuda.dTV_FGP_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.dTV_FGP_GPU_main.restype = ctypes.c_int # return value is int in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) inref_p = inputRef.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) @@ -762,7 +1082,7 @@ def dTV_FGP_GPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) dims = list(inputData.shape)[::-1] @@ -771,37 +1091,62 @@ def dTV_FGP_GPU(inputData, inputRef, lambdaPar, iterationsNumb, epsil, eta, # int dTV_FGP_GPU_main(float *Input, float *InputRef, float *Output, float *infovector, float lambdaPar, # int iterationsNumb, float epsil, float eta, int methodTV, int nonneg, int gpu_device, int N, int M, int Z); - result = cilregcuda.dTV_FGP_GPU_main(in_p, inref_p, out_p, infovector_p, lambdaPar, iterationsNumb, epsil, eta, - methodTV, nonneg, gpu_device, dims[0], dims[1], dims[2]) + result = cilregcuda.dTV_FGP_GPU_main( + in_p, + inref_p, + out_p, + infovector_p, + lambdaPar, + iterationsNumb, + epsil, + eta, + methodTV, + nonneg, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out - def PatchSelect_GPU(inputData, SearchWindow, SimilarWin, NumNeighb, h, gpu_device, H_i=None, H_j=None, H_k=None, Weights=None): + def PatchSelect_GPU( + inputData, + SearchWindow, + SimilarWin, + NumNeighb, + h, + gpu_device, + H_i=None, + H_j=None, + H_k=None, + Weights=None, + ): # int PatchSelect_GPU_main(float *Input, unsigned short *H_i, unsigned short *H_j, float *Weights, # int N, int M, int SearchWindow, int SimilarWin, int NumNeighb, float h, int gpu_device); cilregcuda.PatchSelect_GPU_main.argtypes = [ - ctypes.POINTER(ctypes.c_float), # pointer to the Input array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_i array - ctypes.POINTER(ctypes.c_ushort), # pointer to the H_j array - ctypes.POINTER(ctypes.c_float), # pointer to the Weights array - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # SearchWindow (int) - ctypes.c_int, # SimilarWin (int) - ctypes.c_int, # NumNeighb (int) - ctypes.c_float, # h (float) - ctypes.c_int, # gpu_device (int) + ctypes.POINTER(ctypes.c_float), # pointer to the Input array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_i array + ctypes.POINTER(ctypes.c_ushort), # pointer to the H_j array + ctypes.POINTER(ctypes.c_float), # pointer to the Weights array + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # SearchWindow (int) + ctypes.c_int, # SimilarWin (int) + ctypes.c_int, # NumNeighb (int) + ctypes.c_float, # h (float) + ctypes.c_int, # gpu_device (int) ] - cilregcuda.PatchSelect_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.PatchSelect_GPU_main.restype = ctypes.c_int # return value is int dims = [NumNeighb, inputData.shape[0], inputData.shape[1]] if Weights is None: - Weights = np.zeros(dims, dtype='float32') + Weights = np.zeros(dims, dtype="float32") if H_i is None: - H_i = np.zeros(dims, dtype='uint16') + H_i = np.zeros(dims, dtype="uint16") if H_j is None: - H_j = np.zeros(dims, dtype='uint16') + H_j = np.zeros(dims, dtype="uint16") in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) hi_p = H_i.ctypes.data_as(ctypes.POINTER(ctypes.c_ushort)) @@ -810,7 +1155,18 @@ def PatchSelect_GPU(inputData, SearchWindow, SimilarWin, NumNeighb, h, gpu_devic # int PatchSelect_GPU_main(float *Input, unsigned short *H_i, unsigned short *H_j, float *Weights, # int N, int M, int SearchWindow, int SimilarWin, int NumNeighb, float h, int gpu_device); - result = cilregcuda.PatchSelect_GPU_main(in_p, hj_p, hi_p, weights_p, - dims[2], dims[1], SearchWindow, SimilarWin, NumNeighb, h, gpu_device) + result = cilregcuda.PatchSelect_GPU_main( + in_p, + hj_p, + hi_p, + weights_p, + dims[2], + dims[1], + SearchWindow, + SimilarWin, + NumNeighb, + h, + gpu_device, + ) return H_i, H_j, Weights diff --git a/src/Python/ccpi/filters/diffusion.py b/src/Python/ccpi/filters/diffusion.py index aba567d..83325d0 100644 --- a/src/Python/ccpi/filters/diffusion.py +++ b/src/Python/ccpi/filters/diffusion.py @@ -2,172 +2,258 @@ import numpy as np from .utils import cilreg, cilregcuda -NDF_GPU=None +NDF_GPU = None Diffus4th_GPU = None -def NDF_CPU(inputData, lambdaPar, sigmaPar, iterationsNumb, tau, penaltytype, epsil, out=None, infovector=None): - # float Diffusion_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, +def NDF_CPU( + inputData, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + penaltytype, + epsil, + out=None, + infovector=None, +): + # float Diffusion_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # float sigmaPar, int iterationsNumb, float tau, int penaltytype, float epsil, int dimX, int dimY, int dimZ); cilreg.Diffusion_CPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_float, # sigmaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # tau (float) - ctypes.c_int, # penaltytype (int) - ctypes.c_float, # epsil (float) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_float, # sigmaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # tau (float) + ctypes.c_int, # penaltytype (int) + ctypes.c_float, # epsil (float) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.Diffusion_CPU_main.restype = ctypes.c_float # return value is float + cilreg.Diffusion_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if out is None: out = np.zeros_like(inputData) out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + dims = list(inputData.shape)[::-1] if inputData.ndim == 2: dims.append(1) - - # float Diffusion_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, + + # float Diffusion_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # float sigmaPar, int iterationsNumb, float tau, int penaltytype, float epsil, int dimX, int dimY, int dimZ); - cilreg.Diffusion_CPU_main(in_p, out_p, infovector_p, lambdaPar, sigmaPar, iterationsNumb, tau, - penaltytype, epsil, dims[0], dims[1], dims[2]) + cilreg.Diffusion_CPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + penaltytype, + epsil, + dims[0], + dims[1], + dims[2], + ) return out -def Diffus4th_CPU(inputData, lambdaPar, sigmaPar, iterationsNumb, tau, epsil, out=None, infovector=None): - # float Diffus4th_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, + +def Diffus4th_CPU( + inputData, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + epsil, + out=None, + infovector=None, +): + # float Diffus4th_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # float sigmaPar, int iterationsNumb, float tau, float epsil, int dimX, int dimY, int dimZ); cilreg.Diffus4th_CPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_float, # sigmaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # tau (float) - ctypes.c_float, # epsil (float) - ctypes.c_int, # dimX (int) - ctypes.c_int, # dimY (int) - ctypes.c_int, # dimZ (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_float, # sigmaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # tau (float) + ctypes.c_float, # epsil (float) + ctypes.c_int, # dimX (int) + ctypes.c_int, # dimY (int) + ctypes.c_int, # dimZ (int) ] - cilreg.Diffus4th_CPU_main.restype = ctypes.c_float # return value is float + cilreg.Diffus4th_CPU_main.restype = ctypes.c_float # return value is float in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if out is None: out = np.zeros_like(inputData) out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + dims = list(inputData.shape)[::-1] if inputData.ndim == 2: dims.append(1) - - # float Diffus4th_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, + + # float Diffus4th_CPU_main(float *Input, float *Output, float *infovector, float lambdaPar, # float sigmaPar, int iterationsNumb, float tau, float epsil, int dimX, int dimY, int dimZ); - cilreg.Diffus4th_CPU_main(in_p, out_p, infovector_p, lambdaPar, sigmaPar, iterationsNumb, tau, epsil, dims[0], dims[1], dims[2]) + cilreg.Diffus4th_CPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + epsil, + dims[0], + dims[1], + dims[2], + ) return out if cilregcuda is not None: - def NDF_GPU(inputData, lambdaPar, sigmaPar, iterationsNumb, tau, - penaltytype, epsil, gpu_device, out=None, infovector=None): - # int NonlDiff_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, + + def NDF_GPU( + inputData, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + penaltytype, + epsil, + gpu_device, + out=None, + infovector=None, + ): + # int NonlDiff_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, # int iterationsNumb, float tau, int penaltytype, float epsil, int gpu_device, int N, int M, int Z); cilregcuda.NonlDiff_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_float, # sigmaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # tau (float) - ctypes.c_int, # penaltytype (int) - ctypes.c_float, # epsil (float) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # Z (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_float, # sigmaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # tau (float) + ctypes.c_int, # penaltytype (int) + ctypes.c_float, # epsil (float) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # Z (int) ] - cilregcuda.NonlDiff_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.NonlDiff_GPU_main.restype = ctypes.c_int # return value is int in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if out is None: out = np.zeros_like(inputData) out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + dims = list(inputData.shape)[::-1] if inputData.ndim == 2: dims.append(1) - - # int NonlDiff_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, + + # int NonlDiff_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, # int iterationsNumb, float tau, int penaltytype, float epsil, int gpu_device, int N, int M, int Z); - result = cilregcuda.NonlDiff_GPU_main(in_p, out_p, infovector_p, lambdaPar, - sigmaPar, iterationsNumb, tau, - penaltytype, epsil, gpu_device, - dims[0], dims[1], dims[2]) + result = cilregcuda.NonlDiff_GPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + penaltytype, + epsil, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out - - def Diffus4th_GPU(inputData, lambdaPar, sigmaPar, iterationsNumb, tau, epsil, gpu_device, out=None, infovector=None): - # int Diffus4th_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, + + def Diffus4th_GPU( + inputData, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + epsil, + gpu_device, + out=None, + infovector=None, + ): + # int Diffus4th_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, # int iterationsNumb, float tau, float epsil, int gpu_device, int N, int M, int Z); cilregcuda.Diffus4th_GPU_main.argtypes = [ ctypes.POINTER(ctypes.c_float), # pointer to the Input array ctypes.POINTER(ctypes.c_float), # pointer to the Output array ctypes.POINTER(ctypes.c_float), # pointer to the infovector array - ctypes.c_float, # lambdaPar (float) - ctypes.c_float, # sigmaPar (float) - ctypes.c_int, # iterationsNumb (int) - ctypes.c_float, # tau (float) - ctypes.c_float, # epsil (float) - ctypes.c_int, # gpu_device (int) - ctypes.c_int, # N (int) - ctypes.c_int, # M (int) - ctypes.c_int, # Z (int) + ctypes.c_float, # lambdaPar (float) + ctypes.c_float, # sigmaPar (float) + ctypes.c_int, # iterationsNumb (int) + ctypes.c_float, # tau (float) + ctypes.c_float, # epsil (float) + ctypes.c_int, # gpu_device (int) + ctypes.c_int, # N (int) + ctypes.c_int, # M (int) + ctypes.c_int, # Z (int) ] - cilregcuda.Diffus4th_GPU_main.restype = ctypes.c_int # return value is int + cilregcuda.Diffus4th_GPU_main.restype = ctypes.c_int # return value is int in_p = inputData.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if out is None: out = np.zeros_like(inputData) out_p = out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + if infovector is None: - infovector = np.zeros((2,), dtype='float32') + infovector = np.zeros((2,), dtype="float32") infovector_p = infovector.ctypes.data_as(ctypes.POINTER(ctypes.c_float)) - + dims = list(inputData.shape)[::-1] if inputData.ndim == 2: dims.append(1) - - # int Diffus4th_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, + + # int Diffus4th_GPU_main(float *Input, float *Output, float *infovector, float lambdaPar, float sigmaPar, # int iterationsNumb, float tau, float epsil, int gpu_device, int N, int M, int Z); - result = cilregcuda.Diffus4th_GPU_main(in_p, out_p, infovector_p, lambdaPar, - sigmaPar, iterationsNumb, tau, - epsil, gpu_device, - dims[0], dims[1], dims[2]) + result = cilregcuda.Diffus4th_GPU_main( + in_p, + out_p, + infovector_p, + lambdaPar, + sigmaPar, + iterationsNumb, + tau, + epsil, + gpu_device, + dims[0], + dims[1], + dims[2], + ) return out diff --git a/src/Python/ccpi/filters/regularisers.py b/src/Python/ccpi/filters/regularisers.py index dbb19e0..87fb97d 100644 --- a/src/Python/ccpi/filters/regularisers.py +++ b/src/Python/ccpi/filters/regularisers.py @@ -1,5 +1,6 @@ import numbers from functools import wraps + # CPU regularisers from .TV import TV_ROF_CPU, TV_ROF_GPU from .TV import TV_FGP_CPU, TV_FGP_GPU @@ -18,12 +19,19 @@ def create_wrapper(CPU_func, GPU_func): @wraps(CPU_func) - def wrapper(*args, device='cpu', **kwargs): - if device == 'cpu': + def wrapper(*args, device="cpu", **kwargs): + if device == "cpu": return CPU_func(*args, **kwargs) - elif device == 'gpu' or isinstance(device, numbers.Integral) and cilregcuda is not None: - return GPU_func(*args, gpu_device=0 if device == 'gpu' else device, **kwargs) + elif ( + device == "gpu" + or isinstance(device, numbers.Integral) + and cilregcuda is not None + ): + return GPU_func( + *args, gpu_device=0 if device == "gpu" else device, **kwargs + ) raise KeyError(f"{GPU_func.__name__}: device {device} not available") + return wrapper @@ -34,16 +42,16 @@ def wrapper(*args, device='cpu', **kwargs): from .TV import TV_ENERGY from .TV import TNV -ROF_TV = create_wrapper(TV_ROF_CPU, TV_ROF_GPU) -FGP_TV = create_wrapper(TV_FGP_CPU, TV_FGP_GPU) -PD_TV = create_wrapper(PDTV_CPU, PDTV_GPU) -SB_TV = create_wrapper(SB_TV_CPU, SB_TV_GPU) -PD_TV = create_wrapper(PDTV_CPU, PDTV_GPU) +ROF_TV = create_wrapper(TV_ROF_CPU, TV_ROF_GPU) +FGP_TV = create_wrapper(TV_FGP_CPU, TV_FGP_GPU) +PD_TV = create_wrapper(PDTV_CPU, PDTV_GPU) +SB_TV = create_wrapper(SB_TV_CPU, SB_TV_GPU) +PD_TV = create_wrapper(PDTV_CPU, PDTV_GPU) LLT_ROF = create_wrapper(LLT_ROF_CPU, LLT_ROF_GPU) -TGV = create_wrapper(TGV_CPU, TGV_GPU) +TGV = create_wrapper(TGV_CPU, TGV_GPU) FGP_dTV = create_wrapper(dTV_FGP_CPU, dTV_FGP_GPU) -NDF = create_wrapper(NDF_CPU, NDF_GPU) +NDF = create_wrapper(NDF_CPU, NDF_GPU) Diff4th = create_wrapper(Diffus4th_CPU, Diffus4th_GPU) PatchSelect = create_wrapper(PatchSelect_CPU, PatchSelect_GPU) diff --git a/src/Python/ccpi/filters/utils.py b/src/Python/ccpi/filters/utils.py index f54a98b..3eae9ad 100644 --- a/src/Python/ccpi/filters/utils.py +++ b/src/Python/ccpi/filters/utils.py @@ -4,17 +4,17 @@ import warnings try: - pre = {'Linux': 'lib', 'Windows': '', 'Darwin': 'lib'}[platform.system()] + pre = {"Linux": "lib", "Windows": "", "Darwin": "lib"}[platform.system()] except KeyError: raise ValueError(f"unsupported platform: {platform.system()}") else: - ext = {'Linux': '.so', 'Windows': '.dll', 'Darwin': '.dylib'}[platform.system()] + ext = {"Linux": ".so", "Windows": ".dll", "Darwin": ".dylib"}[platform.system()] _here = os.path.dirname(__file__) -dll = f'{pre}cilreg{ext}' +dll = f"{pre}cilreg{ext}" cilreg = ctypes.cdll.LoadLibrary(os.path.join(_here, dll)) -gpudll = f'{pre}cilregcuda{ext}' +gpudll = f"{pre}cilregcuda{ext}" try: cilregcuda = ctypes.cdll.LoadLibrary(os.path.join(_here, gpudll)) except Exception as exc: diff --git a/src/Python/ccpi/supp/qualitymetrics.py b/src/Python/ccpi/supp/qualitymetrics.py index f44d832..0f4fa5f 100644 --- a/src/Python/ccpi/supp/qualitymetrics.py +++ b/src/Python/ccpi/supp/qualitymetrics.py @@ -5,25 +5,30 @@ """ import numpy as np + class QualityTools: def __init__(self, im1, im2): if im1.size != im2.size: - print ('Error: Sizes of images/volumes are different') + print("Error: Sizes of images/volumes are different") raise SystemExit - self.im1 = im1 # image or volume - 1 - self.im2 = im2 # image or volume - 2 + self.im1 = im1 # image or volume - 1 + self.im2 = im2 # image or volume - 2 + def nrmse(self): - """ Normalised Root Mean Square Error """ + """Normalised Root Mean Square Error""" rmse = np.sqrt(np.sum((self.im2 - self.im1) ** 2) / float(self.im1.size)) max_val = max(np.max(self.im1), np.max(self.im2)) min_val = min(np.min(self.im1), np.min(self.im2)) return 1 - (rmse / (max_val - min_val)) + def rmse(self): - """ Root Mean Square Error """ + """Root Mean Square Error""" rmse = np.sqrt(np.sum((self.im1 - self.im2) ** 2) / float(self.im1.size)) return rmse + def ssim(self, window, k=(0.01, 0.03), l=255): from scipy.signal import fftconvolve + """See https://ece.uwaterloo.ca/~z70wang/research/ssim/""" # Check if the window is smaller than the images. for a, b in zip(window.shape, self.im1.shape): @@ -33,20 +38,20 @@ def ssim(self, window, k=(0.01, 0.03), l=255): for ki in k: if ki < 0: return None, None - + c1 = (k[0] * l) ** 2 c2 = (k[1] * l) ** 2 - window = window/np.sum(window) - - mu1 = fftconvolve(self.im1, window, mode='valid') - mu2 = fftconvolve(self.im2, window, mode='valid') + window = window / np.sum(window) + + mu1 = fftconvolve(self.im1, window, mode="valid") + mu2 = fftconvolve(self.im2, window, mode="valid") mu1_sq = mu1 * mu1 mu2_sq = mu2 * mu2 mu1_mu2 = mu1 * mu2 - sigma1_sq = fftconvolve(self.im1 * self.im1, window, mode='valid') - mu1_sq - sigma2_sq = fftconvolve(self.im2 * self.im2, window, mode='valid') - mu2_sq - sigma12 = fftconvolve(self.im1 * self.im2, window, mode='valid') - mu1_mu2 - + sigma1_sq = fftconvolve(self.im1 * self.im1, window, mode="valid") - mu1_sq + sigma2_sq = fftconvolve(self.im2 * self.im2, window, mode="valid") - mu2_sq + sigma12 = fftconvolve(self.im1 * self.im2, window, mode="valid") - mu1_mu2 + if c1 > 0 and c2 > 0: num = (2 * mu1_mu2 + c1) * (2 * sigma12 + c2) den = (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2) @@ -60,6 +65,6 @@ def ssim(self, window, k=(0.01, 0.03), l=255): index = (den1 * den2) > 0 ssim_map[index] = (num1[index] * num2[index]) / (den1[index] * den2[index]) index = (den1 != 0) & (den2 == 0) - ssim_map[index] = num1[index] / den1[index] + ssim_map[index] = num1[index] / den1[index] mssim = ssim_map.mean() return mssim, ssim_map diff --git a/test/conftest.py b/test/conftest.py index 56e2903..71715ce 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -41,6 +41,10 @@ def pytest_addoption(parser): ) +def pytest_configure(config): + config.addinivalue_line("markers", "cupy: mark test as a CuPy test") + + def pytest_collection_modifyitems(config, items): if config.getoption("--runcupy"): # --runcupy given in cli: do not skip cupy tests diff --git a/test/test_3d_cpu_vs_gpu.py b/test/test_3d_cpu_vs_gpu.py index 4fa1315..141201f 100644 --- a/test/test_3d_cpu_vs_gpu.py +++ b/test/test_3d_cpu_vs_gpu.py @@ -1,4 +1,3 @@ -from numpy._typing._array_like import NDArray import pytest import numpy as np from ccpi.filters.regularisers import ( From 101c6926c7f82d85e025909294f450dd3e61e8ce Mon Sep 17 00:00:00 2001 From: dkazanc Date: Fri, 29 Nov 2024 09:39:56 +0000 Subject: [PATCH 14/15] a slight increase in tolerance for NDF test to pass --- test/test_2d_cpu_vs_gpu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index af23d06..c6cf092 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -636,7 +636,7 @@ def test_NDF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print(txtstr) print("--------Compare the results--------") - eps = 1e-5 + eps = 1e-4 assert_allclose(rms_cpu, rms_gpu, rtol=eps) assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) assert rms_cpu > 0.0 From e2366d76a689b2caab4423e84075b60a45bf77e8 Mon Sep 17 00:00:00 2001 From: dkazanc Date: Fri, 29 Nov 2024 11:16:29 +0000 Subject: [PATCH 15/15] increasing the number of iterations for 3d tests, modifying assertions --- test/test_2d_cpu_vs_gpu.py | 40 +++++++++++------------ test/test_3d_cpu_vs_gpu.py | 66 +++++++++++++++++++------------------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/test/test_2d_cpu_vs_gpu.py b/test/test_2d_cpu_vs_gpu.py index c6cf092..5682d89 100755 --- a/test/test_2d_cpu_vs_gpu.py +++ b/test/test_2d_cpu_vs_gpu.py @@ -53,7 +53,7 @@ def test_ROF_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -97,7 +97,7 @@ def test_ROF_TV_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -146,7 +146,7 @@ def test_FGP_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -193,7 +193,7 @@ def test_FGP_TV_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -247,7 +247,7 @@ def test_PD_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -297,7 +297,7 @@ def test_PD_TV_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -344,7 +344,7 @@ def test_SB_TV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -387,7 +387,7 @@ def test_SB_TV_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -440,7 +440,7 @@ def test_TGV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(tgv_cpu), np.max(tgv_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -538,7 +538,7 @@ def test_LLT_ROF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -584,7 +584,7 @@ def test_LLT_ROF_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -636,8 +636,8 @@ def test_NDF_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print(txtstr) print("--------Compare the results--------") - eps = 1e-4 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -686,7 +686,7 @@ def test_NDF_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -735,8 +735,8 @@ def test_Diff4th_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print(txtstr) print("--------Compare the results--------") - eps = 1e-4 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -781,8 +781,8 @@ def test_Diff4th_CPU_vs_GPU_nonsquare( rms_gpu = rmse(host_pepper_im_nonsquare, diff4th_gpu) print("--------Compare the results--------") - eps = 1e-4 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -839,7 +839,7 @@ def test_FGP_dTV_CPU_vs_GPU(host_pepper_im, host_pepper_im_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -892,7 +892,7 @@ def test_FGP_dTV_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 diff --git a/test/test_3d_cpu_vs_gpu.py b/test/test_3d_cpu_vs_gpu.py index 141201f..5ca9116 100644 --- a/test/test_3d_cpu_vs_gpu.py +++ b/test/test_3d_cpu_vs_gpu.py @@ -21,7 +21,7 @@ def test_ROF_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "algorithm": ROF_TV, "input": host_pepper_3d_noise, "regularisation_parameter": 0.02, - "number_of_iterations": 20, + "number_of_iterations": 100, "time_marching_parameter": 0.001, "tolerance_constant": 0.0, } @@ -51,7 +51,7 @@ def test_ROF_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -68,7 +68,7 @@ def test_ROF_TV_CPU_vs_GPU_noncubic( "algorithm": ROF_TV, "input": host_pepper_3d_noise_noncubic, "regularisation_parameter": 0.02, - "number_of_iterations": 20, + "number_of_iterations": 100, "time_marching_parameter": 0.001, "tolerance_constant": 0.0, } @@ -98,7 +98,7 @@ def test_ROF_TV_CPU_vs_GPU_noncubic( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(rof_cpu), np.max(rof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -113,7 +113,7 @@ def test_FGP_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "algorithm": FGP_TV, "input": host_pepper_3d_noise, "regularisation_parameter": 0.02, - "number_of_iterations": 30, + "number_of_iterations": 100, "tolerance_constant": 0.0, "methodTV": 0, "nonneg": 0, @@ -147,7 +147,7 @@ def test_FGP_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -163,7 +163,7 @@ def test_FGP_TV_CPU_vs_GPU_noncubic( "algorithm": FGP_TV, "input": host_pepper_3d_noise_noncubic, "regularisation_parameter": 0.02, - "number_of_iterations": 30, + "number_of_iterations": 100, "tolerance_constant": 0.0, "methodTV": 0, "nonneg": 0, @@ -194,7 +194,7 @@ def test_FGP_TV_CPU_vs_GPU_noncubic( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_cpu), np.max(fgp_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -211,7 +211,7 @@ def test_PD_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "algorithm": PD_TV, "input": host_pepper_3d_noise, "regularisation_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 100, "tolerance_constant": 0.0, "methodTV": 0, "nonneg": 0, @@ -248,7 +248,7 @@ def test_PD_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -264,7 +264,7 @@ def test_PD_TV_CPU_vs_GPU_noncubic( "algorithm": PD_TV, "input": host_pepper_3d_noise_noncubic, "regularisation_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 100, "tolerance_constant": 0.0, "methodTV": 0, "nonneg": 0, @@ -298,7 +298,7 @@ def test_PD_TV_CPU_vs_GPU_noncubic( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(pd_cpu), np.max(pd_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -315,7 +315,7 @@ def test_SB_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "algorithm": SB_TV, "input": host_pepper_3d_noise, "regularisation_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 80, "tolerance_constant": 0.0, "methodTV": 0, } @@ -345,7 +345,7 @@ def test_SB_TV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -361,7 +361,7 @@ def test_SB_TV_CPU_vs_GPU_noncubic( "algorithm": SB_TV, "input": host_pepper_3d_noise_noncubic, "regularisation_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 80, "tolerance_constant": 0.0, "methodTV": 0, } @@ -388,7 +388,7 @@ def test_SB_TV_CPU_vs_GPU_noncubic( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(sb_cpu), np.max(sb_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -508,7 +508,7 @@ def test_LLT_ROF_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "input": host_pepper_3d_noise, "regularisation_parameterROF": 0.01, "regularisation_parameterLLT": 0.0085, - "number_of_iterations": 50, + "number_of_iterations": 100, "time_marching_parameter": 0.0001, "tolerance_constant": 0.0, } @@ -540,7 +540,7 @@ def test_LLT_ROF_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -557,7 +557,7 @@ def test_LLT_ROF_CPU_vs_GPU_noncubic( "input": host_pepper_3d_noise_noncubic, "regularisation_parameterROF": 0.01, "regularisation_parameterLLT": 0.0085, - "number_of_iterations": 50, + "number_of_iterations": 100, "time_marching_parameter": 0.0001, "tolerance_constant": 0.0, } @@ -589,7 +589,7 @@ def test_LLT_ROF_CPU_vs_GPU_noncubic( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(lltrof_cpu), np.max(lltrof_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -606,7 +606,7 @@ def test_NDF_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "input": host_pepper_3d_noise, "regularisation_parameter": 0.02, "edge_parameter": 0.017, - "number_of_iterations": 50, + "number_of_iterations": 300, "time_marching_parameter": 0.01, "penalty_type": 1, "tolerance_constant": 0.0, @@ -641,7 +641,7 @@ def test_NDF_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -658,7 +658,7 @@ def test_NDF_CPU_vs_GPU_noncubic( "input": host_pepper_3d_noise_noncubic, "regularisation_parameter": 0.02, "edge_parameter": 0.017, - "number_of_iterations": 50, + "number_of_iterations": 300, "time_marching_parameter": 0.01, "penalty_type": 1, "tolerance_constant": 0.0, @@ -689,8 +689,8 @@ def test_NDF_CPU_vs_GPU_noncubic( rms_gpu = rmse(host_pepper_3d_noncubic, ndf_gpu) print("--------Compare the results--------") - eps = 1e-4 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + eps = 1e-5 + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(ndf_cpu), np.max(ndf_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -708,7 +708,7 @@ def test_Diff4th_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "input": host_pepper_3d_noise, "regularisation_parameter": 0.8, "edge_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 150, "time_marching_parameter": 0.0001, "tolerance_constant": 0.0, } @@ -740,7 +740,7 @@ def test_Diff4th_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-4 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -757,7 +757,7 @@ def test_Diff4th_CPU_vs_GPU_nonsquare( "input": host_pepper_3d_noise_noncubic, "regularisation_parameter": 0.8, "edge_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 150, "time_marching_parameter": 0.0001, "tolerance_constant": 0.0, } @@ -786,7 +786,7 @@ def test_Diff4th_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-4 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(diff4th_cpu), np.max(diff4th_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -805,7 +805,7 @@ def test_FGP_dTV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): "input": host_pepper_3d_noise, "refdata": host_pepper_3d, "regularisation_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 100, "tolerance_constant": 0.0, "eta_const": 0.2, "methodTV": 0, @@ -843,7 +843,7 @@ def test_FGP_dTV_CPU_vs_GPU(host_pepper_3d, host_pepper_3d_noise): print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0 @@ -861,7 +861,7 @@ def test_FGP_dTV_CPU_vs_GPU_nonsquare( "input": host_pepper_3d_noise_noncubic, "refdata": host_pepper_3d_noncubic, "regularisation_parameter": 0.02, - "number_of_iterations": 50, + "number_of_iterations": 100, "tolerance_constant": 0.0, "eta_const": 0.2, "methodTV": 0, @@ -896,7 +896,7 @@ def test_FGP_dTV_CPU_vs_GPU_nonsquare( print("--------Compare the results--------") eps = 1e-5 - assert_allclose(rms_cpu, rms_gpu, rtol=eps) + assert_allclose(rms_cpu, rms_gpu, rtol=0, atol=eps) assert_allclose(np.max(fgp_dtv_cpu), np.max(fgp_dtv_gpu), rtol=eps) assert rms_cpu > 0.0 assert rms_gpu > 0.0