diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 912d3a6a..d1eba27d 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 @@ -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 diff --git a/Installation.md b/Installation.md index 5f4712d9..1747030f 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/demos/Matlab_demos/demoMatlab_3Ddenoise.m b/demos/Matlab_demos/demoMatlab_3Ddenoise.m index b7f92cbd..cd84372a 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 3d93cb6b..9524d861 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/SoftwareX_supp/Demo_RealData_Recon_SX.py b/demos/SoftwareX_supp/Demo_RealData_Recon_SX.py index e60af6e6..67975499 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 0925cf69..9b52076c 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 1d3dc038..9790863a 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 cdf43252..01e4dfa8 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 e1281279..d54238ea 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 06b5eb25..b38b4378 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/data/lena_gray_512.tif b/demos/data/lena_gray_512.tif deleted file mode 100644 index f80cafca..00000000 Binary files a/demos/data/lena_gray_512.tif and /dev/null differ diff --git a/demos/demo_cpu_regularisers.py b/demos/demo_cpu_regularisers.py index f0ffe275..df4cd831 100644 --- a/demos/demo_cpu_regularisers.py +++ b/demos/demo_cpu_regularisers.py @@ -12,601 +12,731 @@ 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, TNV, NDF, Diff4th +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 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( "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 = 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) +Im = imread(filename) + +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) # 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") -# 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 -""" -#%% -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -print ("_______________ROF-TV (2D)_________________") -print ("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +plt.figure() +plt.imshow(u0, cmap="gray") +plt.show() +# %% +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} - -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,info_vec_cpu) = ROF_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['time_marching_parameter'], - pars['tolerance_constant'], 'cpu') +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')) +plt.title("{}".format("CPU results")) +# %% +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") +print("_______________FGP-TV (2D)__________________") +print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") -#%% -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,info_vec_cpu) = FGP_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'],'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,info_vec_cpu) = PD_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'], - pars['nonneg'], - pars['lipschitz_const'],'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' :1 ,\ - '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,info_vec_cpu) = SB_TV(pars['input'], - pars['regularisation_parameter'], - pars['number_of_iterations'], - pars['tolerance_constant'], - pars['methodTV'],'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,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') +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 11b7b0b7..1f2b3bc8 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 @@ -13,502 +13,609 @@ 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 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 + + ############################################################################### 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 + + ############################################################################### -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 , - 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') - -# 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 -""" +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 -#%% -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.02 * 100,\ - '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='gpu', 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_cpu_vs_gpu_regularisers.py b/demos/demo_cpu_vs_gpu_regularisers.py deleted file mode 100644 index a63efd2c..00000000 --- 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 f3874e76..24861ea6 100644 --- a/demos/demo_gpu_regularisers.py +++ b/demos/demo_gpu_regularisers.py @@ -12,550 +12,678 @@ 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 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 + + ############################################################################### 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 + + ############################################################################### -#%% -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 = 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)) -(N,M) = np.shape(u0) +Im = imread(filename) + +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) # 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') -""" -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 -""" -#%% +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 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 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 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 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 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 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 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.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 603b1756..8b938a73 100644 --- a/demos/demo_gpu_regularisers3D.py +++ b/demos/demo_gpu_regularisers3D.py @@ -12,491 +12,594 @@ 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 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 + + ############################################################################### 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 + + ############################################################################### -#%% -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 , - 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') -""" -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 -""" - +u0 = u0.astype("float32") +u_ref = u_ref.astype("float32") 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') +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 CPU####################") +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 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 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 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 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 51e00ac0..af0715e9 100644 --- a/demos/demo_gpu_regularisers3D_CuPy.py +++ b/demos/demo_gpu_regularisers3D_CuPy.py @@ -9,145 +9,160 @@ 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 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("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 , - 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 -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)) - 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 = 3000, - tolerance_param = 0.0, - 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 357e38fd..9bda2970 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/pytest.ini b/pytest.ini new file mode 100644 index 00000000..aa39b190 --- /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/bld.bat b/recipe/bld.bat index cd2244f1..6e7f7911 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\" + :: -G "Visual Studio 16 2019" specifies the the generator :: -T v142 specifies the toolset :: -DCUDA_TOOLKIT_ROOT_DIR="C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v11.8" to the cmake command to specify the CUDA toolkit version diff --git a/recipe/build.sh b/recipe/build.sh index 0fedbe27..2940ec4c 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" diff --git a/recipe/meta.yaml b/recipe/meta.yaml index 8347dccf..e1a4d457 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 -s requirements: build: - python @@ -26,10 +26,14 @@ requirements: - pip - 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 9928b64d..59e9f698 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 9928b64d..59e9f698 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/src/Python/ccpi/filters/TV.py b/src/Python/ccpi/filters/TV.py index 1f8aef46..363a4d30 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 aba567dc..83325d04 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 dbb19e0e..87fb97d4 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/regularisersCuPy.py b/src/Python/ccpi/filters/regularisersCuPy.py index 16cf90f5..3a1f70e2 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/src/Python/ccpi/filters/utils.py b/src/Python/ccpi/filters/utils.py index f54a98bb..3eae9ada 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 f44d8327..0f4fa5fa 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/src/Python/pyproject.toml b/src/Python/pyproject.toml index 911f4695..a3fd26c5 100644 --- a/src/Python/pyproject.toml +++ b/src/Python/pyproject.toml @@ -9,6 +9,11 @@ include = ["ccpi", "ccpi.*"] [project] version = "24.0.1" name = "ccpi-regulariser" -dependencies = ["numpy"] +dependencies = [ + "numpy", + "pillow", + "pytest", + "imageio", +] [project.optional-dependencies] gpu = ["cupy"] diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 00000000..71715ce1 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,213 @@ +# Defines common fixtures and makes them available to all tests + +import os +import subprocess +from imageio.v2 import imread + +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_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 + 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") +def data_file(test_data_path): + in_file = os.path.join(test_data_path, "tomo_standard.npz") + 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_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 + 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(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) + + +@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(): + xp.get_default_memory_pool().free_all_blocks() + xp.get_default_pinned_memory_pool().free_all_blocks() + yield None + 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 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 328db770..5682d89d 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,889 @@ 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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + assert rof_cpu.dtype == np.float32 + assert rof_gpu.dtype == np.float32 + print("--------Results match--------") + + +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=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 + 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): + 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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + assert fgp_cpu.dtype == np.float32 + assert fgp_gpu.dtype == np.float32 + print("--------Results match--------") + + +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=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 + assert fgp_cpu.dtype == np.float32 + assert fgp_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + assert pd_cpu.dtype == np.float32 + assert pd_gpu.dtype == np.float32 + print("--------Results match--------") + + +def test_PD_TV_CPU_vs_GPU_nonsquare( + 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=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 + assert pd_cpu.dtype == np.float32 + assert pd_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + assert sb_cpu.dtype == np.float32 + assert sb_gpu.dtype == np.float32 + print("--------Results match--------") + + +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) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + assert sb_cpu.dtype == np.float32 + assert sb_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + 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_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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + assert lltrof_cpu.dtype == np.float32 + assert lltrof_gpu.dtype == np.float32 + print("--------Results match--------") + + +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("--------Compare the results--------") + eps = 1e-5 + 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 + assert lltrof_cpu.dtype == np.float32 + assert lltrof_gpu.dtype == np.float32 + print("--------Results match--------") + + +# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + 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 + 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=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 + 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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + 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 + 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-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 + 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) + pars["rmse"] = rms_gpu + txtstr = printParametersToString(pars) + print(txtstr) + + print("--------Compare the results--------") + eps = 1e-5 + 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 + 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=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 + assert fgp_dtv_cpu.dtype == np.float32 + assert fgp_dtv_gpu.dtype == np.float32 + print("--------Results match--------") diff --git a/test/test_3d_cpu_vs_gpu.py b/test/test_3d_cpu_vs_gpu.py index 30ba582b..5ca91163 100644 --- a/test/test_3d_cpu_vs_gpu.py +++ b/test/test_3d_cpu_vs_gpu.py @@ -1,7 +1,5 @@ -import unittest +import pytest import numpy as np -import os -import timeit from ccpi.filters.regularisers import ( ROF_TV, FGP_TV, @@ -13,163 +11,895 @@ 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() +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": 100, + "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=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 + 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": 100, + "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=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 + 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": 100, + "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=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 + 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": 100, + "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=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 + 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": 100, + "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=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 + 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": 100, + "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=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 + 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": 80, + "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=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 + 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": 80, + "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=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 + 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": 100, + "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=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 + 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": 100, + "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=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 + 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": 300, + "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=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 + 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": 300, + "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-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 + 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": 150, + "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=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 + 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": 150, + "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=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 + 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": 100, + "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=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 + 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": 100, + "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=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 + assert fgp_dtv_cpu.dtype == np.float32 + assert fgp_dtv_gpu.dtype == np.float32 + print("--------Results match--------") diff --git a/test/test_CPU_regularisers.py b/test/test_CPU_regularisers.py deleted file mode 100644 index 725c80ca..00000000 --- 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_cupy_regul.py b/test/test_cupy_regul.py new file mode 100644 index 00000000..b48b43df --- /dev/null +++ b/test/test_cupy_regul.py @@ -0,0 +1,86 @@ +import numpy as np +import pytest + +from numpy.testing import assert_allclose, assert_equal + +eps = 1e-5 + + +@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, + 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): + from ccpi.filters.regularisersCuPy import ROF_TV + + 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): + from ccpi.filters.regularisersCuPy import PD_TV + + filtered_data = PD_TV( + device_data[60, :, :], + regularisation_parameter=0.06, + iterations=1000, + 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): + from ccpi.filters.regularisersCuPy import PD_TV + + filtered_data = PD_TV( + device_data, + regularisation_parameter=0.06, + iterations=1000, + 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 00000000..71576b16 Binary files /dev/null and b/test/test_data/peppers.tif differ 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 diff --git a/test/test_imageLena.bin b/test/test_imageLena.bin deleted file mode 100644 index 9f5c3684..00000000 --- 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 64e56e3b..00000000 --- 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 - - -############################################################################### diff --git a/tests_cupy/conftest.py b/tests_cupy/conftest.py deleted file mode 100644 index a4916b64..00000000 --- a/tests_cupy/conftest.py +++ /dev/null @@ -1,37 +0,0 @@ -# Defines common fixtures and makes them available to all tests - -import os - -import cupy as cp -import numpy as np -import pytest - -CUR_DIR = os.path.abspath(os.path.dirname(__file__)) - -@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") -def data_file(test_data_path): - in_file = os.path.join(test_data_path, "tomo_standard.npz") - return np.load(in_file) - - -@pytest.fixture -def ensure_clean_memory(): - cp.get_default_memory_pool().free_all_blocks() - cp.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() - -@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) diff --git a/tests_cupy/test_ccpi_regularisers_cupy.py b/tests_cupy/test_ccpi_regularisers_cupy.py deleted file mode 100644 index fc2c4fd0..00000000 --- a/tests_cupy/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