From f618466c25d43f3bae9e40920273bf77de1e1149 Mon Sep 17 00:00:00 2001 From: leochanj105 Date: Mon, 19 Oct 2020 23:09:30 -0400 Subject: initial sd-vbs initial sd-vbs add sd-vbs sd-vbs --- SD-VBS/common/c/calcSobel_dX.c | 77 + SD-VBS/common/c/calcSobel_dY.c | 79 + SD-VBS/common/c/extra.h | 500 ++++ SD-VBS/common/c/fCopy.c | 24 + SD-VBS/common/c/fDeepCopy.c | 24 + SD-VBS/common/c/fDeepCopyRange.c | 24 + SD-VBS/common/c/fDivide.c | 23 + SD-VBS/common/c/fFind3.c | 46 + SD-VBS/common/c/fFreeHandle.c | 16 + SD-VBS/common/c/fHorzcat.c | 40 + SD-VBS/common/c/fMallocHandle.c | 18 + SD-VBS/common/c/fMdivide.c | 27 + SD-VBS/common/c/fMinus.c | 21 + SD-VBS/common/c/fMtimes.c | 38 + SD-VBS/common/c/fPlus.c | 21 + SD-VBS/common/c/fResetArray.c | 19 + SD-VBS/common/c/fResetHandle.c | 19 + SD-VBS/common/c/fReshape.c | 27 + SD-VBS/common/c/fResortIndices.c | 77 + SD-VBS/common/c/fSelfCheck.c | 59 + SD-VBS/common/c/fSetArray.c | 22 + SD-VBS/common/c/fSort.c | 43 + SD-VBS/common/c/fSortIndices.c | 77 + SD-VBS/common/c/fSum.c | 55 + SD-VBS/common/c/fSum2.c | 56 + SD-VBS/common/c/fTimes.c | 21 + SD-VBS/common/c/fTranspose.c | 27 + SD-VBS/common/c/fWriteMatrix.c | 34 + SD-VBS/common/c/ffConv2.c | 47 + SD-VBS/common/c/ffConv2_dY.c | 52 + SD-VBS/common/c/ffDivide.c | 21 + SD-VBS/common/c/ffTimes.c | 21 + SD-VBS/common/c/ffVertcat.c | 36 + SD-VBS/common/c/ffiConv2.c | 54 + SD-VBS/common/c/fiConv2.c | 54 + SD-VBS/common/c/fiCopy.c | 23 + SD-VBS/common/c/fiDeepCopy.c | 23 + SD-VBS/common/c/horzcat.c | 48 + SD-VBS/common/c/iCheck.c | 18 + SD-VBS/common/c/iDeepCopy.c | 23 + SD-VBS/common/c/iDeepCopyRange.c | 23 + SD-VBS/common/c/iFreeHandle.c | 16 + SD-VBS/common/c/iHorzcat.c | 41 + SD-VBS/common/c/iMallocHandle.c | 20 + SD-VBS/common/c/iMinus.c | 21 + SD-VBS/common/c/iResetArray.c | 22 + SD-VBS/common/c/iReshape.c | 25 + SD-VBS/common/c/iSetArray.c | 22 + SD-VBS/common/c/iSort.c | 43 + SD-VBS/common/c/iSortIndices.c | 47 + SD-VBS/common/c/iTimes.c | 22 + SD-VBS/common/c/iTranspose.c | 26 + SD-VBS/common/c/iVertcat.c | 34 + SD-VBS/common/c/ifDeepCopy.c | 25 + SD-VBS/common/c/ifMtimes.c | 38 + SD-VBS/common/c/iiConv2.c | 55 + SD-VBS/common/c/imageBlur.c | 67 + SD-VBS/common/c/imageReblur.c | 67 + SD-VBS/common/c/imageResize.c | 78 + SD-VBS/common/c/isMinus.c | 23 + SD-VBS/common/c/isPlus.c | 22 + SD-VBS/common/c/photonEndTiming.c | 22 + SD-VBS/common/c/photonPrintTiming.c | 22 + SD-VBS/common/c/photonReportTiming.c | 28 + SD-VBS/common/c/photonStartTiming.c | 23 + SD-VBS/common/c/randWrapper.c | 30 + SD-VBS/common/c/randnWrapper.c | 40 + SD-VBS/common/c/readFile.c | 42 + SD-VBS/common/c/readImage.c | 112 + SD-VBS/common/c/sdvbs_common.h | 139 + SD-VBS/common/c/selfCheck.c | 65 + SD-VBS/common/c/timingUtils.h | 99 + SD-VBS/common/c/uiFreeHandle.c | 15 + SD-VBS/common/c/uiMallocHandle.c | 20 + SD-VBS/common/c/uiResetArray.c | 19 + SD-VBS/common/c/uiSetArray.c | 21 + SD-VBS/common/c/writeMatrix.c | 34 + SD-VBS/common/makefiles/Makefile.common | 135 + SD-VBS/common/makefiles/Makefile.include | 16 + SD-VBS/common/makefiles/Makefile.recurse | 39 + SD-VBS/common/matlab/cycle.h | 514 ++++ SD-VBS/common/matlab/fSelfCheck.m | 27 + SD-VBS/common/matlab/fWriteMatrix.m | 18 + SD-VBS/common/matlab/photonEndTiming.c | 15 + SD-VBS/common/matlab/photonEndTiming.mexa64 | Bin 0 -> 6686 bytes SD-VBS/common/matlab/photonEndTiming.mexglx | Bin 0 -> 4984 bytes SD-VBS/common/matlab/photonPrintTiming.m | 11 + SD-VBS/common/matlab/photonReportTiming.m | 7 + SD-VBS/common/matlab/photonStartTiming.c | 15 + SD-VBS/common/matlab/photonStartTiming.mexa64 | Bin 0 -> 6688 bytes SD-VBS/common/matlab/photonStartTiming.mexglx | Bin 0 -> 4986 bytes SD-VBS/common/matlab/product.m | 30 + SD-VBS/common/matlab/randWrapper.m | 17 + SD-VBS/common/matlab/randn.m | 22 + SD-VBS/common/matlab/randnWrapper.m | 17 + SD-VBS/common/matlab/readFile.m | 22 + SD-VBS/common/matlab/readImage.m | 60 + SD-VBS/common/matlab/read_image8u_bmp.m | 66 + SD-VBS/common/matlab/reshapeMatrix.m | 13 + SD-VBS/common/matlab/selfCheck.m | 27 + SD-VBS/common/matlab/testProductFunction.m | 21 + SD-VBS/common/matlab/testReshapeFunction.m | 21 + SD-VBS/common/matlab/timingFuncs/cycle.h | 514 ++++ SD-VBS/common/matlab/timingFuncs/photonEndTiming.c | 15 + .../matlab/timingFuncs/photonEndTiming.mexglx | Bin 0 -> 4984 bytes .../common/matlab/timingFuncs/photonPrintTiming.m | 5 + .../common/matlab/timingFuncs/photonReportTiming.m | 7 + .../common/matlab/timingFuncs/photonStartTiming.c | 15 + .../matlab/timingFuncs/photonStartTiming.mexglx | Bin 0 -> 4986 bytes SD-VBS/common/matlab/writeMatrix.m | 19 + SD-VBS/common/matlab/write_image8u_bmp.m | 88 + SD-VBS/common/support/buildTable.py | 54 + SD-VBS/common/support/buildTimeTable.py | 55 + SD-VBS/common/toolbox/MultiNcut/MNcut.m | 93 + SD-VBS/common/toolbox/MultiNcut/MNcutDemo.m | 34 + SD-VBS/common/toolbox/MultiNcut/README.tex | 9 + SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.c | 405 +++ .../common/toolbox/MultiNcut/a_times_b_cmplx.dll | Bin 0 -> 7114 bytes .../toolbox/MultiNcut/a_times_b_cmplx.mexa64 | Bin 0 -> 10646 bytes .../toolbox/MultiNcut/a_times_b_cmplx.mexglx | Bin 0 -> 7896 bytes .../toolbox/MultiNcut/a_times_b_cmplx.mexmac | Bin 0 -> 13096 bytes SD-VBS/common/toolbox/MultiNcut/affinityic.c | 187 ++ SD-VBS/common/toolbox/MultiNcut/affinityic.dll | Bin 0 -> 7680 bytes SD-VBS/common/toolbox/MultiNcut/affinityic.mexa64 | Bin 0 -> 9755 bytes SD-VBS/common/toolbox/MultiNcut/affinityic.mexglx | Bin 0 -> 7775 bytes SD-VBS/common/toolbox/MultiNcut/batch_MNcut.m | 48 + SD-VBS/common/toolbox/MultiNcut/cimgnbmap.c | 198 ++ SD-VBS/common/toolbox/MultiNcut/cimgnbmap.dll | Bin 0 -> 7168 bytes SD-VBS/common/toolbox/MultiNcut/cimgnbmap.mexa64 | Bin 0 -> 9598 bytes SD-VBS/common/toolbox/MultiNcut/cimgnbmap.mexglx | Bin 0 -> 7824 bytes SD-VBS/common/toolbox/MultiNcut/cimgnbmap_cross.c | 197 ++ .../common/toolbox/MultiNcut/cimgnbmap_cross.dll | Bin 0 -> 7168 bytes .../toolbox/MultiNcut/cimgnbmap_cross.mexa64 | Bin 0 -> 9604 bytes .../toolbox/MultiNcut/cimgnbmap_cross.mexglx | Bin 0 -> 7830 bytes SD-VBS/common/toolbox/MultiNcut/cimgnbmap_star.c | 294 ++ SD-VBS/common/toolbox/MultiNcut/cimgnbmap_star.dll | Bin 0 -> 7680 bytes .../common/toolbox/MultiNcut/cimgnbmap_star.mexa64 | Bin 0 -> 9747 bytes .../common/toolbox/MultiNcut/cimgnbmap_star.mexglx | Bin 0 -> 7829 bytes SD-VBS/common/toolbox/MultiNcut/compileAll.m | 10 + SD-VBS/common/toolbox/MultiNcut/computeMultiW.m | 245 ++ SD-VBS/common/toolbox/MultiNcut/discretisation.m | 49 + .../MultiNcut/discretisationEigenVectorData.m | 12 + SD-VBS/common/toolbox/MultiNcut/doog1.m | 32 + SD-VBS/common/toolbox/MultiNcut/doog2.m | 38 + SD-VBS/common/toolbox/MultiNcut/eigSolve.m | 5 + SD-VBS/common/toolbox/MultiNcut/fft_filt_2.m | 29 + SD-VBS/common/toolbox/MultiNcut/gaussian.m | 31 + .../toolbox/MultiNcut/make_filterbank_even2.m | 45 + .../toolbox/MultiNcut/make_filterbank_odd2.m | 46 + .../common/toolbox/MultiNcut/mex_projection_QR.c | 82 + .../common/toolbox/MultiNcut/mex_projection_QR.dll | Bin 0 -> 10240 bytes .../toolbox/MultiNcut/mex_projection_QR.mexa64 | Bin 0 -> 13067 bytes .../toolbox/MultiNcut/mex_projection_QR.mexglx | Bin 0 -> 10165 bytes .../MultiNcut/mex_projection_QR_symmetric.c | 83 + .../MultiNcut/mex_projection_QR_symmetric.dll | Bin 0 -> 10240 bytes .../MultiNcut/mex_projection_QR_symmetric.mexa64 | Bin 0 -> 13093 bytes .../MultiNcut/mex_projection_QR_symmetric.mexglx | Bin 0 -> 10175 bytes .../MultiNcut/mex_w_times_x_symmetric.mexglx | Bin 0 -> 8713 bytes .../MultiNcut/mex_w_times_x_symmetric.mexmac | Bin 0 -> 13396 bytes SD-VBS/common/toolbox/MultiNcut/multiAffinityic.c | 216 ++ .../common/toolbox/MultiNcut/multiAffinityic.dll | Bin 0 -> 8192 bytes .../toolbox/MultiNcut/multiAffinityic.mexa64 | Bin 0 -> 10138 bytes .../toolbox/MultiNcut/multiAffinityic.mexglx | Bin 0 -> 8134 bytes .../toolbox/MultiNcut/multiIntensityFirstLayer.c | 126 + .../toolbox/MultiNcut/multiIntensityFirstLayer.dll | Bin 0 -> 7168 bytes .../MultiNcut/multiIntensityFirstLayer.mexa64 | Bin 0 -> 9345 bytes .../MultiNcut/multiIntensityFirstLayer.mexglx | Bin 0 -> 6805 bytes .../common/toolbox/MultiNcut/multiIntensityWppc.c | 158 + .../toolbox/MultiNcut/multiIntensityWppc.dll | Bin 0 -> 7168 bytes .../toolbox/MultiNcut/multiIntensityWppc.mexa64 | Bin 0 -> 9493 bytes .../toolbox/MultiNcut/multiIntensityWppc.mexglx | Bin 0 -> 7277 bytes SD-VBS/common/toolbox/MultiNcut/quadedgep2.m | 188 ++ .../common/toolbox/MultiNcut/quickNcutHardBiais2.m | 187 ++ SD-VBS/common/toolbox/MultiNcut/read_data.m | 13 + SD-VBS/common/toolbox/MultiNcut/readimage.m | 15 + SD-VBS/common/toolbox/MultiNcut/run_script.m | 60 + SD-VBS/common/toolbox/MultiNcut/showmask.m | 65 + SD-VBS/common/toolbox/MultiNcut/sparsifyc.c | 232 ++ SD-VBS/common/toolbox/MultiNcut/sparsifyc.dll | Bin 0 -> 8704 bytes SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexa64 | Bin 0 -> 10322 bytes SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexglx | Bin 0 -> 8296 bytes SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexmac | Bin 0 -> 9004 bytes SD-VBS/common/toolbox/MultiNcut/spmtimesd.c | 141 + SD-VBS/common/toolbox/MultiNcut/spmtimesd.dll | Bin 0 -> 7168 bytes SD-VBS/common/toolbox/MultiNcut/spmtimesd.mexa64 | Bin 0 -> 9282 bytes SD-VBS/common/toolbox/MultiNcut/spmtimesd.mexglx | Bin 0 -> 7280 bytes SD-VBS/common/toolbox/MultiNcut/spmtimesd.mexmac | Bin 0 -> 8888 bytes SD-VBS/common/toolbox/MultiNcut/tim_eigs.m | 1084 +++++++ SD-VBS/common/toolbox/ikkjin/getANMS.m | 30 + SD-VBS/common/toolbox/ikkjin/getImgGrad.m | 7 + SD-VBS/common/toolbox/ikkjin/harris.m | 43 + SD-VBS/common/toolbox/lagrcv/Makefile | 21 + SD-VBS/common/toolbox/lagrcv/README.sxw | Bin 0 -> 5929 bytes SD-VBS/common/toolbox/lagrcv/calcGradientPyrMex.cc | 45 + .../toolbox/lagrcv/calcGradientPyrMex.mexa64 | Bin 0 -> 28391 bytes .../toolbox/lagrcv/calcGradientPyrMex.mexglx | Bin 0 -> 23141 bytes SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.cc | 30 + SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexa64 | Bin 0 -> 27799 bytes SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexglx | Bin 0 -> 22665 bytes SD-VBS/common/toolbox/lagrcv/calcOptFlowLKMex.cc | 85 + .../common/toolbox/lagrcv/calcOptFlowLKMex.mexa64 | Bin 0 -> 28499 bytes .../common/toolbox/lagrcv/calcOptFlowLKMex.mexglx | Bin 0 -> 23321 bytes .../common/toolbox/lagrcv/calcOptFlowLKPyrMex.cc | 99 + .../toolbox/lagrcv/calcOptFlowLKPyrMex.mexa64 | Bin 0 -> 28778 bytes .../toolbox/lagrcv/calcOptFlowLKPyrMex.mexglx | Bin 0 -> 23692 bytes .../common/toolbox/lagrcv/calcOptFlowLKPyrMex2.cc | 99 + .../toolbox/lagrcv/calcOptFlowLKPyrMex2.mexa64 | Bin 0 -> 28811 bytes .../toolbox/lagrcv/calcOptFlowLKPyrMex2.mexglx | Bin 0 -> 23725 bytes .../toolbox/lagrcv/calcOptFlowLKPyrWInitMex.cc | 91 + .../toolbox/lagrcv/calcOptFlowLKPyrWInitMex.mexa64 | Bin 0 -> 28815 bytes .../toolbox/lagrcv/calcOptFlowLKPyrWInitMex.mexglx | Bin 0 -> 23693 bytes .../toolbox/lagrcv/calcOptFlowLKPyrWInitMex2.cc | 91 + .../lagrcv/calcOptFlowLKPyrWInitMex2.mexa64 | Bin 0 -> 28880 bytes .../lagrcv/calcOptFlowLKPyrWInitMex2.mexglx | Bin 0 -> 23726 bytes .../lagrcv/calcOptFlowLKPyrWInitSobelMex.cc | 87 + .../lagrcv/calcOptFlowLKPyrWInitSobelMex.mexa64 | Bin 0 -> 28724 bytes .../lagrcv/calcOptFlowLKPyrWInitSobelMex.mexglx | Bin 0 -> 23558 bytes SD-VBS/common/toolbox/lagrcv/calcOpticalFlowLK.cc | 59 + .../common/toolbox/lagrcv/calcOpticalFlowPyrLK.cc | 77 + .../toolbox/lagrcv/calcOpticalFlowPyrLK.mexglx | Bin 0 -> 6538 bytes SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.cc | 33 + .../common/toolbox/lagrcv/calcResizedImgMex.mexa64 | Bin 0 -> 27842 bytes .../common/toolbox/lagrcv/calcResizedImgMex.mexglx | Bin 0 -> 22704 bytes SD-VBS/common/toolbox/lagrcv/calcSobelMex.cc | 32 + SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexa64 | Bin 0 -> 27837 bytes SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexglx | Bin 0 -> 22699 bytes SD-VBS/common/toolbox/lagrcv/calcSobelPyrMex.cc | 44 + .../common/toolbox/lagrcv/calcSobelPyrMex.mexa64 | Bin 0 -> 28388 bytes .../common/toolbox/lagrcv/calcSobelPyrMex.mexglx | Bin 0 -> 23138 bytes .../common/toolbox/lagrcv/calcSubsampleAvgMex.cc | 33 + .../toolbox/lagrcv/calcSubsampleAvgMex.mexa64 | Bin 0 -> 27844 bytes .../toolbox/lagrcv/calcSubsampleAvgMex.mexglx | Bin 0 -> 22706 bytes SD-VBS/common/toolbox/lagrcv/calcTextureMex.cc | 53 + SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexa64 | Bin 0 -> 28273 bytes SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexglx | Bin 0 -> 23171 bytes SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.cc | 54 + .../common/toolbox/lagrcv/calcTexturePyrMex.mexa64 | Bin 0 -> 28392 bytes .../common/toolbox/lagrcv/calcTexturePyrMex.mexglx | Bin 0 -> 23226 bytes SD-VBS/common/toolbox/lagrcv/dummyMex.cc | 58 + SD-VBS/common/toolbox/lagrcv/dummyMex.mexa64 | Bin 0 -> 8245 bytes SD-VBS/common/toolbox/lagrcv/dummyMex.mexglx | Bin 0 -> 6329 bytes SD-VBS/common/toolbox/lagrcv/findCornerSubPix.cc | 39 + .../common/toolbox/lagrcv/goodFeaturesToTrack.cc | 62 + .../toolbox/lagrcv/goodFeaturesToTrack.mexglx | Bin 0 -> 6280 bytes SD-VBS/common/toolbox/lagrcv/lagrcv.cpp | 840 ++++++ SD-VBS/common/toolbox/lagrcv/lagrcv.h | 51 + SD-VBS/common/toolbox/lagrcv/liblagrcv.a | Bin 0 -> 26268 bytes SD-VBS/common/toolbox/lagrcv/lk_flow.cc | 76 + SD-VBS/common/toolbox/lagrcv/lk_flow.mexglx | Bin 0 -> 7224 bytes SD-VBS/common/toolbox/lagrcv/test.cc | 34 + SD-VBS/common/toolbox/lagrcv/test/getPyramid.m | 10 + SD-VBS/common/toolbox/lagrcv/test/img0.ppm | 3137 ++++++++++++++++++++ SD-VBS/common/toolbox/lagrcv/test/img1.ppm | 3100 +++++++++++++++++++ SD-VBS/common/toolbox/lagrcv/test/pathdef.m | 284 ++ SD-VBS/common/toolbox/lagrcv/test/test_lk.m | 74 + SD-VBS/common/toolbox/lagrcv/test/test_lk_disp.m | 49 + SD-VBS/common/toolbox/lagrcv/test/test_lk_opencv.m | 47 + SD-VBS/common/toolbox/mex_template.c | 58 + .../toolbox_basic/TOOLBOX_calib/Distor2Calib.m | 391 +++ .../toolbox_basic/TOOLBOX_calib/Rectangle2Square.m | 19 + .../toolbox_basic/TOOLBOX_calib/UnWarpPlane.m | 54 + .../toolbox_basic/TOOLBOX_calib/add_suppress.m | 98 + .../toolbox_basic/TOOLBOX_calib/analyse_error.m | 141 + .../toolbox_basic/TOOLBOX_calib/apply_distortion.m | 137 + .../toolbox_basic/TOOLBOX_calib/calib_gui.m | 117 + .../TOOLBOX_calib/check_active_images.m | 19 + .../TOOLBOX_calib/check_convergence.m | 48 + .../toolbox_basic/TOOLBOX_calib/check_directory.m | 97 + .../TOOLBOX_calib/check_extracted_images.m | 37 + .../toolbox_basic/TOOLBOX_calib/clear_windows.m | 4 + .../toolbox/toolbox_basic/TOOLBOX_calib/clearwin.m | 10 + .../toolbox_basic/TOOLBOX_calib/click_calib.m | 193 ++ .../toolbox_basic/TOOLBOX_calib/click_ima_calib.m | 230 ++ .../TOOLBOX_calib/click_ima_calib3D.m | 482 +++ .../toolbox_basic/TOOLBOX_calib/comp_distortion.m | 38 + .../toolbox_basic/TOOLBOX_calib/comp_distortion2.m | 71 + .../TOOLBOX_calib/comp_distortion_oulu.m | 47 + .../toolbox_basic/TOOLBOX_calib/comp_error_calib.m | 46 + .../TOOLBOX_calib/compute_collineation.m | 66 + .../TOOLBOX_calib/compute_extrinsic.m | 123 + .../TOOLBOX_calib/compute_extrinsic_init.m | 151 + .../TOOLBOX_calib/compute_extrinsic_refine.m | 113 + .../TOOLBOX_calib/compute_homography.m | 163 + .../toolbox_basic/TOOLBOX_calib/cornerfinder.m | 215 ++ .../toolbox_basic/TOOLBOX_calib/count_squares.m | 74 + .../toolbox_basic/TOOLBOX_calib/data_calib.m | 92 + .../toolbox_basic/TOOLBOX_calib/error_analysis.m | 182 ++ .../TOOLBOX_calib/export_calib_data.m | 99 + .../toolbox_basic/TOOLBOX_calib/ext_calib.m | 152 + .../toolbox_basic/TOOLBOX_calib/extract_grid.m | 234 ++ .../TOOLBOX_calib/extract_parameters.m | 46 + .../TOOLBOX_calib/extract_parameters3D.m | 36 + .../TOOLBOX_calib/extrinsic_computation.m | 185 ++ .../toolbox_basic/TOOLBOX_calib/fixallvariables.m | 19 + .../toolbox_basic/TOOLBOX_calib/fixvariable.m | 18 + .../toolbox/toolbox_basic/TOOLBOX_calib/ginput3.m | 216 ++ .../toolbox_basic/TOOLBOX_calib/go_calib_optim.m | 139 + .../TOOLBOX_calib/go_calib_optim_iter.m | 394 +++ .../toolbox_basic/TOOLBOX_calib/ima_read_calib.m | 158 + .../TOOLBOX_calib/init_intrinsic_param.m | 158 + .../toolbox/toolbox_basic/TOOLBOX_calib/is3D.m | 19 + .../toolbox_basic/TOOLBOX_calib/loading_calib.m | 10 + .../toolbox/toolbox_basic/TOOLBOX_calib/loadinr.m | 52 + .../toolbox/toolbox_basic/TOOLBOX_calib/loadpgm.m | 89 + .../toolbox/toolbox_basic/TOOLBOX_calib/loadppm.m | 109 + .../toolbox_basic/TOOLBOX_calib/mean_std_robust.m | 7 + .../toolbox/toolbox_basic/TOOLBOX_calib/mosaic.m | 92 + .../toolbox_basic/TOOLBOX_calib/normalize.m | 43 + .../toolbox/toolbox_basic/TOOLBOX_calib/pgmread.m | 26 + .../toolbox_basic/TOOLBOX_calib/project2_oulu.m | 53 + .../toolbox_basic/TOOLBOX_calib/project_points.m | 276 ++ .../toolbox_basic/TOOLBOX_calib/project_points2.m | 312 ++ .../toolbox_basic/TOOLBOX_calib/projectedGrid.m | 24 + .../toolbox_basic/TOOLBOX_calib/projector_calib.m | 258 ++ .../toolbox/toolbox_basic/TOOLBOX_calib/readras.m | 87 + .../TOOLBOX_calib/recomp_corner_calib.m | 119 + .../toolbox/toolbox_basic/TOOLBOX_calib/rect.m | 127 + .../toolbox_basic/TOOLBOX_calib/reproject_calib.m | 121 + .../toolbox_basic/TOOLBOX_calib/rigid_motion.m | 66 + .../toolbox_basic/TOOLBOX_calib/rodrigues.m | 217 ++ .../toolbox/toolbox_basic/TOOLBOX_calib/rotation.m | 23 + .../TOOLBOX_calib/run_error_analysis.m | 65 + .../toolbox/toolbox_basic/TOOLBOX_calib/saveinr.m | 46 + .../toolbox/toolbox_basic/TOOLBOX_calib/savepgm.m | 22 + .../toolbox/toolbox_basic/TOOLBOX_calib/saveppm.m | 45 + .../toolbox_basic/TOOLBOX_calib/saving_calib.m | 95 + .../TOOLBOX_calib/script_fit_distortion.m | 39 + .../toolbox/toolbox_basic/TOOLBOX_calib/startup.m | 9 + .../toolbox_basic/TOOLBOX_calib/undistort_image.m | 193 ++ .../toolbox_basic/TOOLBOX_calib/willson_convert.m | 89 + .../toolbox_basic/TOOLBOX_calib/willson_read.m | 59 + .../toolbox/toolbox_basic/TOOLBOX_calib/writeras.m | 105 + SD-VBS/common/toolbox/toolbox_basic/affine/README | 5 + .../common/toolbox/toolbox_basic/affine/carve_it.m | 25 + .../toolbox/toolbox_basic/affine/compute_AD.m | 90 + .../toolbox/toolbox_basic/affine/compute_AD_disp.m | 103 + .../toolbox/toolbox_basic/affine/compute_J.m | 31 + .../common/toolbox/toolbox_basic/affine/find_AD.m | 82 + .../common/toolbox/toolbox_basic/affine/find_D.m | 65 + .../toolbox/toolbox_basic/affine/find_center.m | 4 + .../toolbox/toolbox_basic/affine/gen_feature_s.m | 17 + SD-VBS/common/toolbox/toolbox_basic/affine/grad.m | 24 + .../toolbox/toolbox_basic/affine/hemisphere_s.m | 27 + SD-VBS/common/toolbox/toolbox_basic/affine/im.m | 3 + .../common/toolbox/toolbox_basic/affine/iter_AD.m | 26 + .../toolbox/toolbox_basic/affine/m_interp4.m | 49 + .../toolbox/toolbox_basic/affine/norm_inten.m | 11 + .../common/toolbox/toolbox_basic/affine/pan.0.pgm | 53 + .../common/toolbox/toolbox_basic/affine/pan.1.pgm | 59 + .../common/toolbox/toolbox_basic/affine/readpgm.m | 26 + .../toolbox/toolbox_basic/affine/simulation.m | 42 + .../toolbox_basic/affine/sports1_11_28.jpeg | Bin 0 -> 23655 bytes .../toolbox/toolbox_basic/affine/test_affine.m | 33 + SD-VBS/common/toolbox/toolbox_basic/affinityic.c | 186 ++ .../toolbox/toolbox_basic/calib/TOOLBOX_calib.tar | Bin 0 -> 98304 bytes .../toolbox_basic/calib_bouguetj/Distor2Calib.m | 391 +++ .../calib_bouguetj/Multi_Calib_oulu.m | 12 + .../calib_bouguetj/Rectangle2Square.m | 19 + .../toolbox_basic/calib_bouguetj/UnWarpPlane.m | 54 + .../toolbox_basic/calib_bouguetj/add_suppress.m | 91 + .../toolbox_basic/calib_bouguetj/analyse_error.m | 104 + .../toolbox/toolbox_basic/calib_bouguetj/calib.m | 74 + .../toolbox_basic/calib_bouguetj/calib3D_gui.m | 115 + .../toolbox_basic/calib_bouguetj/calib_gui.m | 81 + .../calib_bouguetj/check_active_images.m | 14 + .../calib_bouguetj/check_convergence.m | 17 + .../toolbox_basic/calib_bouguetj/check_planarity.m | 41 + .../toolbox_basic/calib_bouguetj/click_calib.m | 99 + .../toolbox_basic/calib_bouguetj/click_calib3D.m | 79 + .../toolbox_basic/calib_bouguetj/click_ima_calib.m | 218 ++ .../calib_bouguetj/click_ima_calib3D.m | 482 +++ .../toolbox_basic/calib_bouguetj/comp_distortion.m | 38 + .../calib_bouguetj/comp_distortion2.m | 71 + .../calib_bouguetj/comp_distortion_oulu.m | 47 + .../calib_bouguetj/comp_error_calib.m | 40 + .../calib_bouguetj/compute_collineation.m | 66 + .../calib_bouguetj/compute_extrinsic.m | 123 + .../calib_bouguetj/compute_extrinsic_init.m | 149 + .../calib_bouguetj/compute_extrinsic_refine.m | 110 + .../calib_bouguetj/compute_homography.m | 163 + .../toolbox_basic/calib_bouguetj/convert_oulu.m | 35 + .../toolbox_basic/calib_bouguetj/cornerfinder.m | 215 ++ .../toolbox_basic/calib_bouguetj/count_squares.m | 74 + .../toolbox_basic/calib_bouguetj/data_calib.m | 89 + .../toolbox_basic/calib_bouguetj/error_analysis.m | 182 ++ .../toolbox_basic/calib_bouguetj/ext_calib.m | 130 + .../toolbox_basic/calib_bouguetj/extract_grid.m | 227 ++ .../calib_bouguetj/extract_parameters.m | 46 + .../calib_bouguetj/extract_parameters3D.m | 36 + .../calib_bouguetj/extrinsic_computation.m | 173 ++ .../toolbox/toolbox_basic/calib_bouguetj/ginput3.m | 216 ++ .../toolbox_basic/calib_bouguetj/go_calib_optim.m | 60 + .../calib_bouguetj/go_calib_optim3D.m | 264 ++ .../calib_bouguetj/go_calib_optim_cont.m | 142 + .../calib_bouguetj/go_calib_optim_iter.m | 332 +++ .../toolbox_basic/calib_bouguetj/graphout_calib.m | 12 + .../calib_bouguetj/graphout_calib3D.m | 153 + .../toolbox_basic/calib_bouguetj/ima_read_calib.m | 107 + .../calib_bouguetj/init_calib_param.m | 210 ++ .../calib_bouguetj/init_intrinsic_param.m | 153 + .../toolbox/toolbox_basic/calib_bouguetj/is3D.m | 19 + .../toolbox_basic/calib_bouguetj/loading_calib.m | 10 + .../toolbox/toolbox_basic/calib_bouguetj/loadinr.m | 52 + .../toolbox/toolbox_basic/calib_bouguetj/loadpgm.m | 89 + .../toolbox/toolbox_basic/calib_bouguetj/loadppm.m | 101 + .../toolbox_basic/calib_bouguetj/mean_std_robust.m | 7 + .../calib_bouguetj/multi_error_oulu.m | 49 + .../toolbox_basic/calib_bouguetj/normalize.m | 32 + .../toolbox/toolbox_basic/calib_bouguetj/pgmread.m | 26 + .../toolbox_basic/calib_bouguetj/project2_oulu.m | 53 + .../toolbox_basic/calib_bouguetj/project_points.m | 276 ++ .../toolbox_basic/calib_bouguetj/projectedGrid.m | 24 + .../toolbox/toolbox_basic/calib_bouguetj/readras.m | 87 + .../calib_bouguetj/recomp_corner_calib.m | 96 + .../toolbox/toolbox_basic/calib_bouguetj/rect.m | 93 + .../toolbox_basic/calib_bouguetj/reproject_calib.m | 92 + .../toolbox_basic/calib_bouguetj/rigid_motion.m | 66 + .../toolbox_basic/calib_bouguetj/rodrigues.m | 217 ++ .../toolbox_basic/calib_bouguetj/rotation.m | 23 + .../calib_bouguetj/run_error_analysis.m | 65 + .../toolbox/toolbox_basic/calib_bouguetj/saveinr.m | 46 + .../toolbox/toolbox_basic/calib_bouguetj/savepgm.m | 22 + .../toolbox/toolbox_basic/calib_bouguetj/saveppm.m | 25 + .../toolbox_basic/calib_bouguetj/saving_calib.m | 27 + .../calib_bouguetj/script_fit_distortion.m | 39 + .../calib_bouguetj/select_sol_no_center.m | 19 + .../calib_bouguetj/select_sol_no_center3D.m | 20 + .../calib_bouguetj/select_sol_with_center.m | 19 + .../calib_bouguetj/select_sol_with_center3D.m | 20 + .../toolbox/toolbox_basic/calib_bouguetj/startup.m | 9 + .../toolbox/toolbox_basic/calib_bouguetj/test_3d.m | 80 + .../toolbox_basic/calib_bouguetj/undistort_image.m | 88 + .../toolbox_basic/calib_bouguetj/writeras.m | 105 + .../calib_bouguetj2/TOOLBOX_calib.tar | Bin 0 -> 253952 bytes .../common/toolbox/toolbox_basic/common/Ncut_IC.m | 26 + .../toolbox/toolbox_basic/common/Ncut_IC_m.m | 42 + .../toolbox/toolbox_basic/common/Ncut_IC_m2.m | 51 + .../toolbox/toolbox_basic/common/affinityic.c | 186 ++ .../toolbox/toolbox_basic/common/affinityic.mexa64 | Bin 0 -> 10209 bytes .../toolbox/toolbox_basic/common/affinityic.mexglx | Bin 0 -> 8828 bytes .../toolbox/toolbox_basic/common/anisodiff.m | 20 + SD-VBS/common/toolbox/toolbox_basic/common/bin.m | 39 + .../toolbox/toolbox_basic/common/cimgnbmap.c | 189 ++ .../toolbox/toolbox_basic/common/cimgnbmap.mexa64 | Bin 0 -> 9679 bytes .../toolbox/toolbox_basic/common/cimgnbmap.mexglx | Bin 0 -> 8486 bytes .../common/toolbox/toolbox_basic/common/density.m | 133 + .../toolbox/toolbox_basic/common/find_edge.m | 24 + SD-VBS/common/toolbox/toolbox_basic/common/grad.m | 28 + .../toolbox_basic/common/make_filterbank_even2.m | 45 + .../toolbox_basic/common/make_filterbank_odd2.m | 46 + .../toolbox/toolbox_basic/common/max_supress2.m | 59 + SD-VBS/common/toolbox/toolbox_basic/common/mgrad.m | 11 + .../common/toolbox/toolbox_basic/common/mpgread.m | 25 + .../toolbox/toolbox_basic/common/mpgread.mexlx | Bin 0 -> 84735 bytes .../common/toolbox/toolbox_basic/common/mpgwrite.m | 29 + .../toolbox/toolbox_basic/common/mpgwrite.mexlx | Bin 0 -> 105791 bytes SD-VBS/common/toolbox/toolbox_basic/common/ncut.m | 108 + .../common/toolbox/toolbox_basic/common/ncut_b.m | 46 + .../common/toolbox/toolbox_basic/common/ncut_bb.m | 39 + .../common/toolbox/toolbox_basic/common/ncut_e.m | 36 + .../common/toolbox/toolbox_basic/common/ncut_neg.m | 45 + .../toolbox/toolbox_basic/common/ncut_sparse.m | 45 + .../common/toolbox/toolbox_basic/common/ncut_tmp.m | 45 + SD-VBS/common/toolbox/toolbox_basic/common/ncutd.m | 108 + .../toolbox/toolbox_basic/common/nonmaxsup.m | 81 + .../toolbox/toolbox_basic/common/pair_dist.m | 14 + .../toolbox/toolbox_basic/common/quadedgep.m | 106 + .../common/toolbox/toolbox_basic/common/readpcm.m | 12 + .../common/toolbox/toolbox_basic/common/readpdm.m | 8 + .../common/toolbox/toolbox_basic/common/readpfm.m | 10 + .../toolbox/toolbox_basic/common/renormalize.m | 32 + .../toolbox/toolbox_basic/common/show_edge.m | 11 + .../toolbox/toolbox_basic/common/spmtimesd.c | 141 + .../toolbox/toolbox_basic/common/spmtimesd.mexa64 | Bin 0 -> 9427 bytes .../toolbox/toolbox_basic/common/spmtimesd.mexglx | Bin 0 -> 8198 bytes SD-VBS/common/toolbox/toolbox_basic/common/tmp.tex | 16 + .../common/toolbox/toolbox_basic/common/vmquant.m | 112 + .../toolbox/toolbox_basic/common/vmquantc.mexhp7 | Bin 0 -> 24603 bytes .../toolbox/toolbox_basic/common/vmquantc.mexlx | Bin 0 -> 16987 bytes .../toolbox/toolbox_basic/common/vmquantc.mexsol | Bin 0 -> 27012 bytes .../common/toolbox/toolbox_basic/common/writepdm.m | 11 + .../common/toolbox/toolbox_basic/common/writepfm.m | 11 + .../common/toolbox/toolbox_basic/disp/disp_image.m | 19 + .../common/toolbox/toolbox_basic/disp/draw_box.m | 9 + .../common/toolbox/toolbox_basic/disp/draw_box2.m | 17 + SD-VBS/common/toolbox/toolbox_basic/disp/im.m | 8 + SD-VBS/common/toolbox/toolbox_basic/disp/ims.m | 3 + .../common/toolbox/toolbox_basic/disp/montage2.m | 17 + .../common/toolbox/toolbox_basic/disp/showmask.m | 20 + .../common/toolbox/toolbox_basic/disp/showmaskb.m | 20 + .../toolbox/toolbox_basic/fact/construct_w.m | 25 + .../toolbox/toolbox_basic/fact/construct_w2.m | 25 + SD-VBS/common/toolbox/toolbox_basic/fact/factor.m | 50 + .../toolbox/toolbox_basic/fact/factor_test.m | 52 + .../toolbox/toolbox_basic/fact/factor_test2.m | 52 + .../toolbox/toolbox_basic/fact/factorizaion.tar | Bin 0 -> 81920 bytes SD-VBS/common/toolbox/toolbox_basic/fact/findG.m | 48 + SD-VBS/common/toolbox/toolbox_basic/fact/findg1.m | 49 + SD-VBS/common/toolbox/toolbox_basic/fact/findg2.m | 56 + SD-VBS/common/toolbox/toolbox_basic/fact/hotel.mat | Bin 0 -> 56320 bytes .../toolbox/toolbox_basic/fact/show_3dpoints.m | 22 + SD-VBS/common/toolbox/toolbox_basic/fact/show_S.m | 17 + SD-VBS/common/toolbox/toolbox_basic/fact/show_t.m | 10 + SD-VBS/common/toolbox/toolbox_basic/fact/show_t3.m | 10 + SD-VBS/common/toolbox/toolbox_basic/fact/zt.m | 6 + .../common/toolbox/toolbox_basic/filter/91048.jpg | Bin 0 -> 2553 bytes SD-VBS/common/toolbox/toolbox_basic/filter/bar2d.m | 16 + .../common/toolbox/toolbox_basic/filter/barrot.m | 22 + SD-VBS/common/toolbox/toolbox_basic/filter/bars.m | 39 + .../toolbox/toolbox_basic/filter/clip_image.m | 6 + .../toolbox_basic/filter/compute_J_simple.m | 50 + .../toolbox/toolbox_basic/filter/compute_angle.m | 18 + .../toolbox_basic/filter/compute_filter_fft.m | 84 + .../toolbox/toolbox_basic/filter/compute_g2.m | 23 + .../toolbox/toolbox_basic/filter/compute_h2.m | 27 + .../toolbox_basic/filter/compute_ofilter_fft.m | 88 + .../common/toolbox/toolbox_basic/filter/dgauss.m | 16 + SD-VBS/common/toolbox/toolbox_basic/filter/dog1.m | 28 + SD-VBS/common/toolbox/toolbox_basic/filter/dog2.m | 31 + SD-VBS/common/toolbox/toolbox_basic/filter/doog1.m | 32 + SD-VBS/common/toolbox/toolbox_basic/filter/doog2.m | 38 + .../common/toolbox/toolbox_basic/filter/fft_filt.m | 82 + .../toolbox/toolbox_basic/filter/fft_filt_2.m | 29 + .../toolbox_basic/filter/filter_bank_jshi.tar | Bin 0 -> 71680 bytes SD-VBS/common/toolbox/toolbox_basic/filter/gauss.m | 16 + .../common/toolbox/toolbox_basic/filter/gaussian.m | 31 + .../toolbox/toolbox_basic/filter/get_diff2.m | 43 + .../toolbox/toolbox_basic/filter/get_diff_free.m | 8 + SD-VBS/common/toolbox/toolbox_basic/filter/grad1.m | 11 + SD-VBS/common/toolbox/toolbox_basic/filter/grad2.m | 11 + .../toolbox/toolbox_basic/filter/m_interp4.m | 49 + .../toolbox/toolbox_basic/filter/make_filterbank.m | 63 + .../toolbox_basic/filter/make_filterbank_23.m | 40 + .../toolbox_basic/filter/make_filterbank_even.m | 40 + .../toolbox_basic/filter/make_filterbank_odd.m | 41 + .../common/toolbox/toolbox_basic/filter/mdoog2.m | 36 + .../toolbox/toolbox_basic/filter/mimrotate.m | 119 + .../toolbox/toolbox_basic/filter/mk_odd_filter.m | 36 + .../common/toolbox/toolbox_basic/filter/mkdog1.m | 20 + .../common/toolbox/toolbox_basic/filter/mkdog2.m | 22 + .../common/toolbox/toolbox_basic/filter/mkdoog2.m | 30 + .../common/toolbox/toolbox_basic/filter/mkdoogs.m | 15 + SD-VBS/common/toolbox/toolbox_basic/filter/mkg.m | 9 + .../common/toolbox/toolbox_basic/filter/quadpair.m | 20 + .../common/toolbox/toolbox_basic/filter/smooth.m | 24 + .../toolbox/toolbox_basic/filter/softkmean.m | 56 + .../toolbox/toolbox_basic/filter/softmeans.m | 46 + .../toolbox/toolbox_basic/filter/softmeans2.m | 39 + SD-VBS/common/toolbox/toolbox_basic/filterQuad.zip | Bin 0 -> 4531 bytes .../toolbox/toolbox_basic/filter_hist/1d_cut.m | 16 + .../toolbox/toolbox_basic/filter_hist/Bfilter.m | 11 + .../toolbox/toolbox_basic/filter_hist/BfilterS.m | 17 + .../toolbox/toolbox_basic/filter_hist/Ncut.m | 14 + .../toolbox_basic/filter_hist/apply_image.m | 38 + .../toolbox/toolbox_basic/filter_hist/back_proj.m | 10 + .../toolbox_basic/filter_hist/backproj_outer.m | 9 + .../filter_hist/backproj_outer_chank.m | 33 + .../filter_hist/backproj_outer_chank2.m | 36 + .../toolbox/toolbox_basic/filter_hist/binize.m | 15 + .../toolbox/toolbox_basic/filter_hist/binize_old.m | 34 + .../toolbox_basic/filter_hist/binomialfield.m | 75 + .../toolbox/toolbox_basic/filter_hist/colize.m | 9 + .../toolbox_basic/filter_hist/colize_hist.m | 29 + .../toolbox_basic/filter_hist/colize_histnb_s.m | 47 + .../toolbox_basic/filter_hist/colize_histnb_sf.m | 52 + .../toolbox_basic/filter_hist/colize_histneighb.m | 37 + .../toolbox_basic/filter_hist/colize_joint_hist.m | 41 + .../toolbox_basic/filter_hist/colize_test.m | 19 + .../toolbox/toolbox_basic/filter_hist/compact.m | 36 + .../toolbox/toolbox_basic/filter_hist/compute_J.m | 31 + .../toolbox/toolbox_basic/filter_hist/compute_Lf.m | 35 + .../toolbox_basic/filter_hist/compute_corr.m | 10 + .../toolbox_basic/filter_hist/compute_diff.m | 36 + .../toolbox_basic/filter_hist/compute_diff_patch.m | 34 + .../filter_hist/compute_diff_patch2.m | 45 + .../toolbox_basic/filter_hist/compute_filter.m | 84 + .../toolbox_basic/filter_hist/compute_filter_fft.m | 84 + .../toolbox/toolbox_basic/filter_hist/conv_trim.m | 6 + .../toolbox/toolbox_basic/filter_hist/corr_hist.m | 9 + .../toolbox_basic/filter_hist/crop_im_fil.m | 11 + .../toolbox/toolbox_basic/filter_hist/cutoff.m | 13 + .../toolbox/toolbox_basic/filter_hist/cutout.m | 3 + .../toolbox/toolbox_basic/filter_hist/disp_Imask.m | 20 + .../toolbox/toolbox_basic/filter_hist/disp_diff.m | 37 + .../toolbox_basic/filter_hist/disp_evresult.m | 435 +++ .../toolbox_basic/filter_hist/disp_evresult2.m | 215 ++ .../toolbox_basic/filter_hist/disp_evresulthome.m | 237 ++ .../toolbox_basic/filter_hist/disp_groups.m | 13 + .../toolbox_basic/filter_hist/disp_hist2d.m | 15 + .../toolbox/toolbox_basic/filter_hist/dist_pair.m | 28 + .../toolbox_basic/filter_hist/dist_pair_chank.m | 25 + .../toolbox/toolbox_basic/filter_hist/doog2.m | 43 + .../toolbox/toolbox_basic/filter_hist/eig_decomp.m | 15 + .../toolbox_basic/filter_hist/eig_decomp_v5.m | 13 + .../toolbox/toolbox_basic/filter_hist/eig_proj.m | 12 + .../toolbox_basic/filter_hist/eigs_decomp.m | 39 + .../toolbox_basic/filter_hist/euclid_dist.m | 22 + .../toolbox_basic/filter_hist/filter_all_files.m | 29 + .../toolbox_basic/filter_hist/filter_output.m | 38 + .../toolbox_basic/filter_hist/find_bst_cut.m | 24 + .../toolbox_basic/filter_hist/find_center.m | 4 + .../toolbox_basic/filter_hist/find_cutpoint.m | 13 + .../toolbox_basic/filter_hist/gen_filters.m | 47 + .../toolbox_basic/filter_hist/get_cumhist.m | 9 + .../toolbox_basic/filter_hist/get_cumhist_inten.m | 7 + .../toolbox/toolbox_basic/filter_hist/get_hist.m | 24 + .../toolbox_basic/filter_hist/get_hist_inten.m | 15 + .../toolbox/toolbox_basic/filter_hist/get_win.m | 10 + .../toolbox/toolbox_basic/filter_hist/get_win5.m | 11 + .../toolbox/toolbox_basic/filter_hist/grad.m | 24 + .../toolbox_basic/filter_hist/half_sigmoid.m | 17 + .../toolbox/toolbox_basic/filter_hist/hist2d.m | 13 + .../toolbox/toolbox_basic/filter_hist/hist_I_f.m | 22 + .../toolbox/toolbox_basic/filter_hist/hist_diff.m | 30 + .../toolbox/toolbox_basic/filter_hist/hist_f.m | 28 + .../toolbox_basic/filter_hist/hist_in_chank.m | 33 + .../toolbox/toolbox_basic/filter_hist/hist_inner.m | 40 + .../toolbox_basic/filter_hist/histbin_fv_chank.m | 14 + .../toolbox/toolbox_basic/filter_hist/hsv2clrs.m | 25 + .../toolbox/toolbox_basic/filter_hist/id_cut.m | 14 + .../common/toolbox/toolbox_basic/filter_hist/im.m | 3 + .../common/toolbox/toolbox_basic/filter_hist/im3.m | 3 + .../common/toolbox/toolbox_basic/filter_hist/im5.m | 16 + .../toolbox/toolbox_basic/filter_hist/im_vect.m | 20 + .../toolbox/toolbox_basic/filter_hist/imrotate.m | 119 + .../common/toolbox/toolbox_basic/filter_hist/ims.m | 3 + .../toolbox/toolbox_basic/filter_hist/imvs.m | 4 + .../toolbox/toolbox_basic/filter_hist/is_step.m | 33 + .../toolbox/toolbox_basic/filter_hist/ks_2d.m | 20 + .../toolbox_basic/filter_hist/load_result.m | 39 + .../toolbox/toolbox_basic/filter_hist/m_interp4.m | 49 + .../toolbox/toolbox_basic/filter_hist/make_masks.m | 12 + .../toolbox/toolbox_basic/filter_hist/makefilter.m | 14 + .../toolbox/toolbox_basic/filter_hist/mkf_t1.m | 22 + .../toolbox/toolbox_basic/filter_hist/mkf_t2.m | 21 + .../toolbox/toolbox_basic/filter_hist/mkf_test.m | 43 + .../common/toolbox/toolbox_basic/filter_hist/mkg.m | 9 + .../toolbox/toolbox_basic/filter_hist/mkgaussian.m | 11 + .../toolbox_basic/filter_hist/mkmulfilter.m | 52 + .../toolbox/toolbox_basic/filter_hist/mkpoog2.m | 29 + .../toolbox/toolbox_basic/filter_hist/mksgn.m | 10 + .../toolbox/toolbox_basic/filter_hist/mksgn2.m | 9 + .../toolbox/toolbox_basic/filter_hist/mreadpfm.m | 11 + .../toolbox/toolbox_basic/filter_hist/mreadpfm2.m | 9 + .../toolbox/toolbox_basic/filter_hist/mwis.m | 16 + .../toolbox/toolbox_basic/filter_hist/mwis_image.m | 25 + .../toolbox/toolbox_basic/filter_hist/myinterp.m | 18 + .../toolbox/toolbox_basic/filter_hist/ncut_b.m | 25 + .../toolbox_basic/filter_hist/new_compute_J.m | 32 + .../toolbox/toolbox_basic/filter_hist/pair_dist.m | 45 + .../toolbox/toolbox_basic/filter_hist/pair_dist2.m | 46 + .../toolbox_basic/filter_hist/pair_dist_text.m | 70 + .../toolbox_basic/filter_hist/pair_dist_text2.m | 58 + .../toolbox_basic/filter_hist/pair_dist_text3.m | 84 + .../toolbox_basic/filter_hist/pair_dist_text4.m | 81 + .../toolbox/toolbox_basic/filter_hist/patch_cat.m | 9 + .../toolbox/toolbox_basic/filter_hist/pgmread.m | 15 + .../toolbox/toolbox_basic/filter_hist/poisson.m | 44 + .../toolbox_basic/filter_hist/poissonfield.m | 53 + .../toolbox/toolbox_basic/filter_hist/proj_back.m | 24 + .../toolbox_basic/filter_hist/proj_back_id.m | 19 + .../toolbox/toolbox_basic/filter_hist/quant.m | 20 + .../toolbox/toolbox_basic/filter_hist/readpdm.m | 8 + .../toolbox/toolbox_basic/filter_hist/readpfm.m | 10 + .../toolbox/toolbox_basic/filter_hist/readpfm_id.m | 21 + .../toolbox_basic/filter_hist/readpfm_idf.m | 29 + .../toolbox/toolbox_basic/filter_hist/readpgm.m | 26 + .../toolbox/toolbox_basic/filter_hist/readpnm.m | 21 + .../toolbox/toolbox_basic/filter_hist/readppm.m | 19 + .../toolbox/toolbox_basic/filter_hist/record.m | 6 + .../toolbox_basic/filter_hist/recursive_cut_tc.m | 140 + .../toolbox/toolbox_basic/filter_hist/reduce_all.m | 8 + .../toolbox/toolbox_basic/filter_hist/rotate_J.m | 30 + .../toolbox/toolbox_basic/filter_hist/session.m | 4 + .../toolbox_basic/filter_hist/show_cumhist.m | 28 + .../toolbox/toolbox_basic/filter_hist/show_hist.m | 23 + .../toolbox/toolbox_basic/filter_hist/showsmm.m | 45 + .../toolbox/toolbox_basic/filter_hist/showsmm_v5.m | 34 + .../toolbox/toolbox_basic/filter_hist/sigmoid.m | 10 + .../toolbox/toolbox_basic/filter_hist/signif.m | 22 + .../toolbox/toolbox_basic/filter_hist/signif_N.m | 10 + .../toolbox/toolbox_basic/filter_hist/smooth.m | 20 + .../toolbox/toolbox_basic/filter_hist/startup.m | 9 + .../toolbox/toolbox_basic/filter_hist/swarp.m | 9 + .../toolbox/toolbox_basic/filter_hist/swarpback.m | 12 + .../toolbox/toolbox_basic/filter_hist/test.m | 110 + .../toolbox/toolbox_basic/filter_hist/test1.m | 175 ++ .../toolbox/toolbox_basic/filter_hist/test2.m | 220 ++ .../toolbox/toolbox_basic/filter_hist/test3.m | 4 + .../toolbox_basic/filter_hist/test_best_cut.m | 12 + .../toolbox/toolbox_basic/filter_hist/test_evtex.m | 361 +++ .../toolbox_basic/filter_hist/test_evtex2.m | 136 + .../toolbox_basic/filter_hist/test_evtex3.m | 169 ++ .../toolbox_basic/filter_hist/test_evtex4.m | 353 +++ .../toolbox_basic/filter_hist/test_evtex5.m | 446 +++ .../toolbox_basic/filter_hist/test_motion.m | 117 + .../toolbox_basic/filter_hist/test_motion2.m | 127 + .../toolbox_basic/filter_hist/test_period.m | 58 + .../toolbox/toolbox_basic/filter_hist/test_text.m | 434 +++ .../common/toolbox/toolbox_basic/filter_hist/tmp.m | 68 + .../toolbox/toolbox_basic/filter_hist/tmp1.m | 25 + .../toolbox/toolbox_basic/filter_hist/tmp2.m | 17 + .../toolbox/toolbox_basic/filter_hist/tmp3.m | 126 + .../toolbox/toolbox_basic/filter_hist/true_loc.m | 23 + .../toolbox/toolbox_basic/filter_hist/vmquant.m | 112 + .../toolbox/toolbox_basic/filter_hist/wismm.m | 26 + .../toolbox/toolbox_basic/filter_hist/wismm2.m | 66 + .../toolbox/toolbox_basic/filter_hist/wismm3.m | 71 + .../toolbox_basic/filter_hist/write_command.m | 8 + .../toolbox/toolbox_basic/filter_hist/write_test.m | 38 + .../toolbox_basic/filter_hist/writeout_feature.m | 40 + .../toolbox/toolbox_basic/filter_hist/writepfm.m | 11 + .../toolbox/toolbox_basic/filter_hist/writepgm.m | 8 + .../toolbox/toolbox_basic/filter_hist/writepmm.m | 14 + .../toolbox/toolbox_basic/filter_hist/writepnm5.m | 26 + .../toolbox/toolbox_basic/filtersQuad/doog1.m | 32 + .../toolbox/toolbox_basic/filtersQuad/doog2.m | 38 + .../filtersQuad/make_filterbank_even2.m | 45 + .../filtersQuad/make_filterbank_odd2.m | 46 + .../toolbox/toolbox_basic/filtersQuad/quadedgep2.m | 188 ++ .../common/toolbox/toolbox_basic/io/convert422.m | 27 + SD-VBS/common/toolbox/toolbox_basic/io/im_vd.m | 6 + SD-VBS/common/toolbox/toolbox_basic/io/imread2.m | 45 + .../toolbox/toolbox_basic/io/peek_pgm_size.m | 19 + SD-VBS/common/toolbox/toolbox_basic/io/pgmread.m | 24 + SD-VBS/common/toolbox/toolbox_basic/io/ppmtojpg.m | 25 + SD-VBS/common/toolbox/toolbox_basic/io/read422.m | 45 + SD-VBS/common/toolbox/toolbox_basic/io/read422f.m | 50 + .../common/toolbox/toolbox_basic/io/read_cimgs.m | 40 + .../common/toolbox/toolbox_basic/io/read_ev_pgm.m | 26 + .../common/toolbox/toolbox_basic/io/read_ev_pgm2.m | 29 + .../toolbox/toolbox_basic/io/read_ev_pgm_real.m | 30 + SD-VBS/common/toolbox/toolbox_basic/io/read_imgs.m | 47 + SD-VBS/common/toolbox/toolbox_basic/io/read_pmm.m | 12 + SD-VBS/common/toolbox/toolbox_basic/io/read_scan.m | 42 + .../toolbox/toolbox_basic/io/read_seg_file.m | 36 + SD-VBS/common/toolbox/toolbox_basic/io/readlines.m | 30 + SD-VBS/common/toolbox/toolbox_basic/io/readpdm3.m | 16 + SD-VBS/common/toolbox/toolbox_basic/io/readpdmc.m | 12 + SD-VBS/common/toolbox/toolbox_basic/io/readpfm.m | 10 + SD-VBS/common/toolbox/toolbox_basic/io/readpfm3.m | 17 + SD-VBS/common/toolbox/toolbox_basic/io/readpfmc.m | 11 + SD-VBS/common/toolbox/toolbox_basic/io/readpgm.m | 30 + .../toolbox/toolbox_basic/io/readpgm_evinfo.m | 35 + SD-VBS/common/toolbox/toolbox_basic/io/readpmm.m | 22 + SD-VBS/common/toolbox/toolbox_basic/io/readppm.m | 23 + SD-VBS/common/toolbox/toolbox_basic/io/writepgm.m | 8 + SD-VBS/common/toolbox/toolbox_basic/io/writeppm.m | 14 + .../pub/contrib/v5/optim/assignprob/Contents.m | 26 + .../pub/contrib/v5/optim/assignprob/allcosts.m | 17 + .../pub/contrib/v5/optim/assignprob/allperm.m | 17 + .../pub/contrib/v5/optim/assignprob/condass.m | 54 + .../pub/contrib/v5/optim/assignprob/demo.m | 38 + .../pub/contrib/v5/optim/assignprob/hungarian.m | 464 +++ .../pub/contrib/v5/optim/assignprob/test.m | 87 + .../common/toolbox/toolbox_basic/matlab_tools.zip | Bin 0 -> 26722 bytes .../toolbox_basic/pyramid/091399fbn-jets.3.jpg | Bin 0 -> 25886 bytes .../common/toolbox/toolbox_basic/pyramid/expand.m | 8 + .../toolbox/toolbox_basic/pyramid/gauss_lowpass.m | 9 + .../common/toolbox/toolbox_basic/pyramid/gen_w.m | 12 + .../common/toolbox/toolbox_basic/pyramid/reduce.m | 7 + .../common/toolbox/toolbox_basic/pyramid/session.m | 26 + .../common/toolbox/toolbox_basic/pyramid/startup.m | 5 + SD-VBS/common/toolbox/toolbox_basic/remap_angle.m | 4 + SD-VBS/common/toolbox/toolbox_basic/spmtimesd.c | 141 + .../toolbox/toolbox_basic/stella/afromncut.m | 73 + .../common/toolbox/toolbox_basic/stella/dispimg.m | 65 + .../toolbox/toolbox_basic/stella/firstncut.m | 67 + .../toolbox/toolbox_basic/stella/getfnames.m | 47 + .../toolbox/toolbox_basic/stella/getimage2.m | 46 + .../toolbox/toolbox_basic/stella/globalenvar.m | 6 + .../common/toolbox/toolbox_basic/stella/jshincut.m | 94 + .../toolbox/toolbox_basic/stella/jshincutdefpar.m | 20 + .../toolbox/toolbox_basic/stella/ncutcheckin.m | 136 + .../toolbox/toolbox_basic/stella/openfigure.m | 52 + .../common/toolbox/toolbox_basic/stella/showim.m | 36 + .../common/toolbox/toolbox_basic/stella/showncut.m | 92 + .../common/toolbox/toolbox_basic/stella/startup.m | 18 + .../toolbox/toolbox_basic/stella/test_ncutm.m | 38 + .../toolbox/toolbox_basic/tars/TOOLBOX_calib.tar | Bin 0 -> 253952 bytes .../common/toolbox/toolbox_basic/textons/dist2.m | 27 + .../toolbox/toolbox_basic/textons/find_textons.m | 46 + .../toolbox/toolbox_basic/textons/find_textons1.m | 37 + .../common/toolbox/toolbox_basic/textons/kmeans2.m | 127 + 784 files changed, 50526 insertions(+) create mode 100644 SD-VBS/common/c/calcSobel_dX.c create mode 100644 SD-VBS/common/c/calcSobel_dY.c create mode 100644 SD-VBS/common/c/extra.h create mode 100644 SD-VBS/common/c/fCopy.c create mode 100644 SD-VBS/common/c/fDeepCopy.c create mode 100644 SD-VBS/common/c/fDeepCopyRange.c create mode 100644 SD-VBS/common/c/fDivide.c create mode 100644 SD-VBS/common/c/fFind3.c create mode 100644 SD-VBS/common/c/fFreeHandle.c create mode 100644 SD-VBS/common/c/fHorzcat.c create mode 100644 SD-VBS/common/c/fMallocHandle.c create mode 100644 SD-VBS/common/c/fMdivide.c create mode 100644 SD-VBS/common/c/fMinus.c create mode 100644 SD-VBS/common/c/fMtimes.c create mode 100644 SD-VBS/common/c/fPlus.c create mode 100644 SD-VBS/common/c/fResetArray.c create mode 100644 SD-VBS/common/c/fResetHandle.c create mode 100644 SD-VBS/common/c/fReshape.c create mode 100644 SD-VBS/common/c/fResortIndices.c create mode 100644 SD-VBS/common/c/fSelfCheck.c create mode 100644 SD-VBS/common/c/fSetArray.c create mode 100644 SD-VBS/common/c/fSort.c create mode 100644 SD-VBS/common/c/fSortIndices.c create mode 100644 SD-VBS/common/c/fSum.c create mode 100644 SD-VBS/common/c/fSum2.c create mode 100644 SD-VBS/common/c/fTimes.c create mode 100644 SD-VBS/common/c/fTranspose.c create mode 100644 SD-VBS/common/c/fWriteMatrix.c create mode 100644 SD-VBS/common/c/ffConv2.c create mode 100644 SD-VBS/common/c/ffConv2_dY.c create mode 100644 SD-VBS/common/c/ffDivide.c create mode 100644 SD-VBS/common/c/ffTimes.c create mode 100644 SD-VBS/common/c/ffVertcat.c create mode 100644 SD-VBS/common/c/ffiConv2.c create mode 100644 SD-VBS/common/c/fiConv2.c create mode 100644 SD-VBS/common/c/fiCopy.c create mode 100644 SD-VBS/common/c/fiDeepCopy.c create mode 100644 SD-VBS/common/c/horzcat.c create mode 100644 SD-VBS/common/c/iCheck.c create mode 100644 SD-VBS/common/c/iDeepCopy.c create mode 100644 SD-VBS/common/c/iDeepCopyRange.c create mode 100644 SD-VBS/common/c/iFreeHandle.c create mode 100644 SD-VBS/common/c/iHorzcat.c create mode 100644 SD-VBS/common/c/iMallocHandle.c create mode 100644 SD-VBS/common/c/iMinus.c create mode 100644 SD-VBS/common/c/iResetArray.c create mode 100644 SD-VBS/common/c/iReshape.c create mode 100644 SD-VBS/common/c/iSetArray.c create mode 100644 SD-VBS/common/c/iSort.c create mode 100644 SD-VBS/common/c/iSortIndices.c create mode 100644 SD-VBS/common/c/iTimes.c create mode 100644 SD-VBS/common/c/iTranspose.c create mode 100644 SD-VBS/common/c/iVertcat.c create mode 100644 SD-VBS/common/c/ifDeepCopy.c create mode 100644 SD-VBS/common/c/ifMtimes.c create mode 100644 SD-VBS/common/c/iiConv2.c create mode 100644 SD-VBS/common/c/imageBlur.c create mode 100644 SD-VBS/common/c/imageReblur.c create mode 100644 SD-VBS/common/c/imageResize.c create mode 100644 SD-VBS/common/c/isMinus.c create mode 100644 SD-VBS/common/c/isPlus.c create mode 100644 SD-VBS/common/c/photonEndTiming.c create mode 100644 SD-VBS/common/c/photonPrintTiming.c create mode 100644 SD-VBS/common/c/photonReportTiming.c create mode 100644 SD-VBS/common/c/photonStartTiming.c create mode 100644 SD-VBS/common/c/randWrapper.c create mode 100644 SD-VBS/common/c/randnWrapper.c create mode 100644 SD-VBS/common/c/readFile.c create mode 100644 SD-VBS/common/c/readImage.c create mode 100644 SD-VBS/common/c/sdvbs_common.h create mode 100644 SD-VBS/common/c/selfCheck.c create mode 100644 SD-VBS/common/c/timingUtils.h create mode 100644 SD-VBS/common/c/uiFreeHandle.c create mode 100644 SD-VBS/common/c/uiMallocHandle.c create mode 100644 SD-VBS/common/c/uiResetArray.c create mode 100644 SD-VBS/common/c/uiSetArray.c create mode 100644 SD-VBS/common/c/writeMatrix.c create mode 100644 SD-VBS/common/makefiles/Makefile.common create mode 100644 SD-VBS/common/makefiles/Makefile.include create mode 100644 SD-VBS/common/makefiles/Makefile.recurse create mode 100644 SD-VBS/common/matlab/cycle.h create mode 100644 SD-VBS/common/matlab/fSelfCheck.m create mode 100644 SD-VBS/common/matlab/fWriteMatrix.m create mode 100644 SD-VBS/common/matlab/photonEndTiming.c create mode 100755 SD-VBS/common/matlab/photonEndTiming.mexa64 create mode 100755 SD-VBS/common/matlab/photonEndTiming.mexglx create mode 100644 SD-VBS/common/matlab/photonPrintTiming.m create mode 100644 SD-VBS/common/matlab/photonReportTiming.m create mode 100644 SD-VBS/common/matlab/photonStartTiming.c create mode 100755 SD-VBS/common/matlab/photonStartTiming.mexa64 create mode 100755 SD-VBS/common/matlab/photonStartTiming.mexglx create mode 100755 SD-VBS/common/matlab/product.m create mode 100644 SD-VBS/common/matlab/randWrapper.m create mode 100644 SD-VBS/common/matlab/randn.m create mode 100644 SD-VBS/common/matlab/randnWrapper.m create mode 100644 SD-VBS/common/matlab/readFile.m create mode 100644 SD-VBS/common/matlab/readImage.m create mode 100755 SD-VBS/common/matlab/read_image8u_bmp.m create mode 100755 SD-VBS/common/matlab/reshapeMatrix.m create mode 100644 SD-VBS/common/matlab/selfCheck.m create mode 100755 SD-VBS/common/matlab/testProductFunction.m create mode 100755 SD-VBS/common/matlab/testReshapeFunction.m create mode 100644 SD-VBS/common/matlab/timingFuncs/cycle.h create mode 100644 SD-VBS/common/matlab/timingFuncs/photonEndTiming.c create mode 100755 SD-VBS/common/matlab/timingFuncs/photonEndTiming.mexglx create mode 100644 SD-VBS/common/matlab/timingFuncs/photonPrintTiming.m create mode 100644 SD-VBS/common/matlab/timingFuncs/photonReportTiming.m create mode 100644 SD-VBS/common/matlab/timingFuncs/photonStartTiming.c create mode 100755 SD-VBS/common/matlab/timingFuncs/photonStartTiming.mexglx create mode 100644 SD-VBS/common/matlab/writeMatrix.m create mode 100755 SD-VBS/common/matlab/write_image8u_bmp.m create mode 100644 SD-VBS/common/support/buildTable.py create mode 100644 SD-VBS/common/support/buildTimeTable.py create mode 100755 SD-VBS/common/toolbox/MultiNcut/MNcut.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/MNcutDemo.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/README.tex create mode 100755 SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.mexmac create mode 100755 SD-VBS/common/toolbox/MultiNcut/affinityic.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/affinityic.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/affinityic.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/affinityic.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/batch_MNcut.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_cross.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_cross.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_cross.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_cross.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_star.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_star.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_star.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/cimgnbmap_star.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/compileAll.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/computeMultiW.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/discretisation.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/discretisationEigenVectorData.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/doog1.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/doog2.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/eigSolve.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/fft_filt_2.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/gaussian.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/make_filterbank_even2.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/make_filterbank_odd2.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR_symmetric.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR_symmetric.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR_symmetric.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_projection_QR_symmetric.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_w_times_x_symmetric.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/mex_w_times_x_symmetric.mexmac create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiAffinityic.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiAffinityic.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityFirstLayer.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityFirstLayer.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityFirstLayer.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityFirstLayer.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/quadedgep2.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/quickNcutHardBiais2.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/read_data.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/readimage.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/run_script.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/showmask.m create mode 100755 SD-VBS/common/toolbox/MultiNcut/sparsifyc.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/sparsifyc.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexmac create mode 100755 SD-VBS/common/toolbox/MultiNcut/spmtimesd.c create mode 100755 SD-VBS/common/toolbox/MultiNcut/spmtimesd.dll create mode 100755 SD-VBS/common/toolbox/MultiNcut/spmtimesd.mexa64 create mode 100755 SD-VBS/common/toolbox/MultiNcut/spmtimesd.mexglx create mode 100755 SD-VBS/common/toolbox/MultiNcut/spmtimesd.mexmac create mode 100755 SD-VBS/common/toolbox/MultiNcut/tim_eigs.m create mode 100755 SD-VBS/common/toolbox/ikkjin/getANMS.m create mode 100755 SD-VBS/common/toolbox/ikkjin/getImgGrad.m create mode 100755 SD-VBS/common/toolbox/ikkjin/harris.m create mode 100755 SD-VBS/common/toolbox/lagrcv/Makefile create mode 100755 SD-VBS/common/toolbox/lagrcv/README.sxw create mode 100755 SD-VBS/common/toolbox/lagrcv/calcGradientPyrMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcGradientPyrMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcGradientPyrMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrMex2.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrMex2.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrMex2.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitMex2.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitMex2.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitMex2.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitSobelMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitSobelMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOptFlowLKPyrWInitSobelMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOpticalFlowLK.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSobelMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSobelPyrMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSobelPyrMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSobelPyrMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcTextureMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/dummyMex.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/dummyMex.mexa64 create mode 100755 SD-VBS/common/toolbox/lagrcv/dummyMex.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/findCornerSubPix.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/lagrcv.cpp create mode 100755 SD-VBS/common/toolbox/lagrcv/lagrcv.h create mode 100755 SD-VBS/common/toolbox/lagrcv/liblagrcv.a create mode 100755 SD-VBS/common/toolbox/lagrcv/lk_flow.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/lk_flow.mexglx create mode 100755 SD-VBS/common/toolbox/lagrcv/test.cc create mode 100755 SD-VBS/common/toolbox/lagrcv/test/getPyramid.m create mode 100755 SD-VBS/common/toolbox/lagrcv/test/img0.ppm create mode 100755 SD-VBS/common/toolbox/lagrcv/test/img1.ppm create mode 100755 SD-VBS/common/toolbox/lagrcv/test/pathdef.m create mode 100755 SD-VBS/common/toolbox/lagrcv/test/test_lk.m create mode 100755 SD-VBS/common/toolbox/lagrcv/test/test_lk_disp.m create mode 100755 SD-VBS/common/toolbox/lagrcv/test/test_lk_opencv.m create mode 100755 SD-VBS/common/toolbox/mex_template.c create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/Distor2Calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/Rectangle2Square.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/UnWarpPlane.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/add_suppress.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/analyse_error.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/apply_distortion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/calib_gui.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_active_images.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_convergence.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_directory.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_extracted_images.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clear_windows.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clearwin.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion_oulu.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_error_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_collineation.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_init.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_refine.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_homography.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/cornerfinder.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/count_squares.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/data_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/error_analysis.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/export_calib_data.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ext_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_grid.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extrinsic_computation.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixallvariables.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixvariable.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ginput3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim_iter.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ima_read_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/init_intrinsic_param.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/is3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loading_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadinr.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadpgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadppm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mean_std_robust.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mosaic.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/normalize.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/pgmread.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project2_oulu.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projectedGrid.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projector_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/readras.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/recomp_corner_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rect.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/reproject_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rigid_motion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rodrigues.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rotation.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/run_error_analysis.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveinr.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/savepgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveppm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saving_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/script_fit_distortion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/startup.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/undistort_image.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_convert.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_read.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/writeras.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/README create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/carve_it.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD_disp.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/compute_J.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/find_AD.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/find_D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/find_center.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/gen_feature_s.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/grad.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/hemisphere_s.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/im.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/iter_AD.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/m_interp4.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/norm_inten.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/pan.0.pgm create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/pan.1.pgm create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/readpgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/simulation.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/sports1_11_28.jpeg create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affine/test_affine.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/affinityic.c create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib/TOOLBOX_calib.tar create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Distor2Calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Multi_Calib_oulu.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Rectangle2Square.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/UnWarpPlane.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/add_suppress.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/analyse_error.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib3D_gui.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib_gui.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_active_images.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_convergence.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_planarity.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion_oulu.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_error_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_collineation.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_init.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_refine.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_homography.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/convert_oulu.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/cornerfinder.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/count_squares.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/data_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/error_analysis.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ext_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_grid.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extrinsic_computation.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ginput3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_cont.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_iter.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ima_read_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_calib_param.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_intrinsic_param.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/is3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loading_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadinr.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadpgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadppm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/mean_std_robust.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/multi_error_oulu.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/normalize.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/pgmread.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project2_oulu.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project_points.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/projectedGrid.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/readras.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/recomp_corner_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rect.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/reproject_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rigid_motion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rodrigues.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rotation.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/run_error_analysis.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveinr.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/savepgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveppm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saving_calib.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/script_fit_distortion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center3D.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/startup.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/test_3d.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/undistort_image.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/writeras.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj2/TOOLBOX_calib.tar create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/affinityic.c create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexa64 create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexglx create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/anisodiff.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/bin.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/cimgnbmap.c create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/cimgnbmap.mexa64 create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/cimgnbmap.mexglx create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/density.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/find_edge.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/grad.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_even2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_odd2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/max_supress2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/mgrad.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/mpgread.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/mpgread.mexlx create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/mpgwrite.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/mpgwrite.mexlx create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncut_b.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncut_bb.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncut_e.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncut_neg.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncut_sparse.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncut_tmp.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/ncutd.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/nonmaxsup.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/pair_dist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/quadedgep.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/readpcm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/readpdm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/readpfm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/renormalize.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/show_edge.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/spmtimesd.c create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/spmtimesd.mexa64 create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/spmtimesd.mexglx create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/tmp.tex create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/vmquant.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/vmquantc.mexhp7 create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/vmquantc.mexlx create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/vmquantc.mexsol create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/writepdm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/common/writepfm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/disp_image.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/draw_box.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/draw_box2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/im.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/ims.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/montage2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/showmask.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/disp/showmaskb.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/construct_w.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/construct_w2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/factor.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/factor_test.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/factor_test2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/factorizaion.tar create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/findG.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/findg1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/findg2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/hotel.mat create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/show_3dpoints.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/show_S.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/show_t.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/show_t3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/fact/zt.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/91048.jpg create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/bar2d.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/barrot.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/bars.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/clip_image.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/compute_J_simple.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/compute_angle.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/compute_filter_fft.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/compute_g2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/compute_h2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/compute_ofilter_fft.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/dgauss.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/dog1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/dog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/doog1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/doog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt_2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/filter_bank_jshi.tar create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/gauss.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/gaussian.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/get_diff2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/get_diff_free.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/grad1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/grad2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/m_interp4.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_23.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_even.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_odd.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mdoog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mimrotate.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mk_odd_filter.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mkdog1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mkdog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mkdoog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mkdoogs.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/mkg.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/quadpair.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/smooth.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/softkmean.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/softmeans.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter/softmeans2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filterQuad.zip create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/1d_cut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/Bfilter.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/BfilterS.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/Ncut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/apply_image.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/back_proj.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/backproj_outer.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/backproj_outer_chank.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/backproj_outer_chank2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize_old.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/binomialfield.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_hist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_s.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_sf.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histneighb.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_joint_hist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_test.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compact.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_J.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_Lf.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_corr.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter_fft.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/conv_trim.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/corr_hist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/crop_im_fil.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutoff.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutout.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_Imask.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_diff.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresulthome.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_groups.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_hist2d.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair_chank.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/doog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp_v5.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_proj.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/eigs_decomp.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/euclid_dist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_all_files.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_output.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_bst_cut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_center.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_cutpoint.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/gen_filters.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist_inten.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist_inten.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win5.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/grad.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/half_sigmoid.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist2d.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_I_f.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_diff.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_f.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_in_chank.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_inner.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/histbin_fv_chank.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/hsv2clrs.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/id_cut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/im.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/im3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/im5.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/im_vect.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/imrotate.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/ims.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/imvs.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/is_step.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/ks_2d.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/load_result.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/m_interp4.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/make_masks.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/makefilter.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_test.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkg.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkgaussian.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkmulfilter.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkpoog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mksgn.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mksgn2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis_image.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/myinterp.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/ncut_b.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/new_compute_J.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text4.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/patch_cat.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/pgmread.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/poisson.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/poissonfield.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back_id.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/quant.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpdm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_id.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_idf.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpnm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/readppm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/record.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/recursive_cut_tc.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/reduce_all.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/rotate_J.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/session.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_cumhist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_hist.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm_v5.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/sigmoid.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif_N.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/smooth.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/startup.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarp.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarpback.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_best_cut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex4.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex5.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_period.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_text.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/true_loc.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/vmquant.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_command.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_test.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/writeout_feature.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepfm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepmm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepnm5.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_even2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_odd2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/filtersQuad/quadedgep2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/convert422.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/im_vd.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/imread2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/peek_pgm_size.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/pgmread.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/ppmtojpg.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read422.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read422f.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_cimgs.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm_real.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_imgs.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_pmm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_scan.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/read_seg_file.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readlines.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpdm3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpdmc.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpfm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpfm3.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpfmc.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpgm_evinfo.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readpmm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/readppm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/writepgm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/io/writeppm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/Contents.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allcosts.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allperm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/condass.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/demo.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/hungarian.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/test.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/matlab_tools.zip create mode 100755 SD-VBS/common/toolbox/toolbox_basic/pyramid/091399fbn-jets.3.jpg create mode 100755 SD-VBS/common/toolbox/toolbox_basic/pyramid/expand.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/pyramid/gauss_lowpass.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/pyramid/gen_w.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/pyramid/reduce.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/pyramid/session.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/pyramid/startup.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/remap_angle.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/spmtimesd.c create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/afromncut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/dispimg.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/firstncut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/getfnames.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/getimage2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/globalenvar.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/jshincut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/jshincutdefpar.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/ncutcheckin.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/openfigure.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/showim.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/showncut.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/startup.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/stella/test_ncutm.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/tars/TOOLBOX_calib.tar create mode 100755 SD-VBS/common/toolbox/toolbox_basic/textons/dist2.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/textons/find_textons.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/textons/find_textons1.m create mode 100755 SD-VBS/common/toolbox/toolbox_basic/textons/kmeans2.m (limited to 'SD-VBS/common') diff --git a/SD-VBS/common/c/calcSobel_dX.c b/SD-VBS/common/c/calcSobel_dX.c new file mode 100644 index 0000000..4be5845 --- /dev/null +++ b/SD-VBS/common/c/calcSobel_dX.c @@ -0,0 +1,77 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include "sdvbs_common.h" + +F2D* calcSobel_dX(F2D* imageIn) +{ + int rows, cols; + F2D *kernel_1, *kernel_2; + float temp; + int kernelSize, startCol, endCol, halfKernel, startRow, endRow, i, j, kernelSum; + int k, kernelSum_1, kernelSum_2; + F2D *imageOut, *tempOut; + + rows = imageIn->height; + cols = imageIn->width; + + imageOut = fSetArray(rows, cols, 0); + tempOut = fSetArray(rows, cols, 0); + kernel_1 = fMallocHandle(1, 3); + kernel_2 = fMallocHandle(1, 3); + + asubsref(kernel_1,0) = 1; + asubsref(kernel_1,1) = 2; + asubsref(kernel_1,2) = 1; + + kernelSize = 3; + kernelSum_1 = 4; + + asubsref(kernel_2,0) = 1; + asubsref(kernel_2,1) = 0; + asubsref(kernel_2,2) = -1; + + kernelSum_2 = 2; + + startCol = 1; + endCol = cols - 1; + halfKernel = 1; + + startRow = 1; + endRow = rows - 1; + + for(i=startRow; iheight; + cols = imageIn->width; + + // level 1 is the base image. + + outputRows = rows; + outputCols = cols; + + imageOut = fSetArray(outputRows, outputCols, 0); + tempOut = fSetArray(outputRows, outputCols, 0); + kernel_1 = iMallocHandle(1, 3); + kernel_2 = iMallocHandle(1, 3); + + asubsref(kernel_1,0) = 1; + asubsref(kernel_1,1) = 0; + asubsref(kernel_1,2) = -1; + kernelSize = 3; + kernelSum_1 = 2.0; + + asubsref(kernel_2,0) = 1; + asubsref(kernel_2,1) = 2; + asubsref(kernel_2,2) = 1; + kernelSum_2 = 4; + + startCol = 1; + endCol = cols - 1; + halfKernel = 1; + + startRow = 1; + endRow = rows - 1; + + for(i=startRow; i // For O_CREAT and O_RDWR +#include // For sched_yield() +#include // For sem_{open, post, wait}() +#include +#include // For exit() +#include // For strlen() +#include // For mlockall() +#include // For ftruncate() +#include + +// This is only visible if _GNU_SOURCE is defined, and that define does not +// come along to places where this file is included. Address this by manually +// forcing it into the global namespace. +extern int sched_getcpu(); + +// These constants correspond to the imx6q-sabredb platform +#define LINE_SIZE 32 +#define L2_SIZE 16*2048*32 + +#if __arm__ +#include +#include +#endif + +#define LITMUS 0 +#define MC2 0 +#define MMDC_PROF 0 + +#if LITMUS +#include +#endif + +#if MMDC_PROF +#include "/media/speedy/litmus/tools/mmdc/mmdc.h" +#endif + +#if LITMUS +#define SET_UP LOAD_PARAMS SETUP_LITMUS +#else +#define SET_UP LOAD_PARAMS +#endif + +#if MMDC_PROF +#define LOAD_PARAMS LOAD_PARAMS_ITRL SETUP_MMDC +#else +#define LOAD_PARAMS LOAD_PARAMS_ITRL +#endif + +// Store state globally so that the job can be outside main() +// Arrays use float as a comprimise between overflow and size +// Paired arrays use long longs as precision is more important for those times +#ifdef PAIRED +long long *_rt_start_time; +long long *_rt_end_time; +#else +float *_rt_exec_time; +#endif +#if MMDC_PERF +float *_rt_mmdc_read; +float *_rt_mmdc_write; +#endif +long _rt_jobs_complete; +long _rt_max_jobs; +int _rt_core; +int _rt_will_output; +struct timespec _rt_start, _rt_end; + +char *_rt_run_id; +char *_rt_our_prog_name; +char *_rt_other_prog_name; +char *_rt_other_core; +#define _RT_FILENAME_LEN 64 +#define _BILLION (1000*1000*1000) +#ifdef PAIRED +char *_rt_barrier; +sem_t *_rt_first_sem, *_rt_second_sem; +int _rt_lock_id; +#endif + +static void _rt_load_params_itrl(int argc, char **argv) { +#ifdef PAIRED + if (argc != 8) { + fprintf(stderr, "Usage: %s ", argv[0]); + fprintf(stderr, " string for logging. Name of this task.\n"); + fprintf(stderr, " integer number of iterations. -1 for infinite.\n"); + fprintf(stderr, " UNUSED. Core is now auto-detected.\n"); + fprintf(stderr, " integer for logging. Core of paired task.\n"); + fprintf(stderr, " string for logging. Name of paired task.\n"); + fprintf(stderr, " string to append with .txt to yield output file name.\n"); + fprintf(stderr, " 1 to indicate this is pair member 1, otherwise pair member 2.\n"); + exit(1); + } +#else + if (argc != 6) { + fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, " string for logging. Name of this task.\n"); + fprintf(stderr, " integer number of iterations. -1 for infinite.\n"); + fprintf(stderr, " UNUSED. Core is now auto-detected.\n"); + fprintf(stderr, " string to append with .txt to yield output file name.\n"); + fprintf(stderr, " 1 to save results, 0 to discard.\n"); + exit(1); + } +#endif + _rt_our_prog_name = argv[1]; + _rt_max_jobs = atol(argv[2]); + _rt_core = sched_getcpu(); +#ifdef PAIRED + _rt_other_core = argv[4]; + _rt_other_prog_name = argv[5]; + _rt_run_id = argv[6]; + _rt_lock_id = atoi(argv[7]); + // The paired version doesn't support disabling output (legacy compatibility) + _rt_will_output = 1; +#else + _rt_other_core = "none"; + _rt_other_prog_name = "none"; + _rt_run_id = argv[4]; + _rt_will_output = atoi(argv[5]); +#endif /* PAIRED */ + if (_rt_max_jobs < 0 && _rt_will_output != 0) { + fprintf(stderr, "Infinite loops only supported when _rt_will_output is disabled!\n"); + exit(1); + } + if (strlen(_rt_run_id) + 5 > _RT_FILENAME_LEN) { + fprintf(stderr, "Run ID is too large! Keep it to less than %d characters.\n", _RT_FILENAME_LEN); + exit(1); + } +#ifdef PAIRED + _rt_start_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(long long)); + _rt_end_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(long long)); + if (!_rt_end_time || !_rt_start_time) { + perror("Unable to allocate buffers for execution times"); + exit(1); + } + _rt_first_sem = sem_open("/_libextra_first_sem", O_CREAT, 644, 0); + _rt_second_sem = sem_open("/_libextra_second_sem", O_CREAT, 644, 0); + if (_rt_first_sem == SEM_FAILED || _rt_second_sem == SEM_FAILED) { + perror("Error while creating semaphores"); + exit(1); + } + int barrier_file = shm_open("/_libextra_barrier", O_CREAT | O_RDWR, 644); + if (barrier_file == -1) { + perror("Error while creating shared memory for barrier synchronization"); + exit(1); + } + if (ftruncate(barrier_file, 1) == -1) { + perror("Error while setting size of shared memory for barrier synchronization"); + exit(1); + } + _rt_barrier = mmap(NULL, 1, PROT_WRITE, MAP_SHARED, barrier_file, 0); + if (_rt_barrier == MAP_FAILED) { + perror("Error while mapping shared memory for barrier synchronization"); + exit(1); + } + *_rt_barrier = 0; +#else + _rt_exec_time = calloc(_rt_max_jobs * _rt_will_output, sizeof(float)); + if (!_rt_exec_time) { + perror("Unable to allocate buffer for execution times"); + exit(1); + } +#endif /* PAIRED */ + _rt_jobs_complete = 0; + mlockall(MCL_CURRENT || MCL_FUTURE); +} +#define LOAD_PARAMS_ITRL _rt_load_params_itrl(argc, argv); + +#define SETUP_MMDC \ + _rt_mmdc_read = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ + _rt_mmdc_write = calloc(_rt_max_jobs * _rt_will_output, sizeof(float));\ + if (!_rt_mmdc_read || !_rt_mmdc_write) {\ + perror("Unable to allocate buffer for MMDC data");\ + exit(1);\ + }\ + MMDC_PROFILE_RES_t mmdc_res;\ + memset(&mmdc_res, 0, sizeof(MMDC_PROFILE_RES_t));\ + int fd = open("/dev/mem", O_RDWR, 0);\ + if (fd < 0) {\ + perror("Unable to open /dev/mem");\ + exit(1);\ + }\ + pMMDC_t mmdc = mmap(NULL, 0x4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, MMDC_P0_IPS_BASE_ADDR);\ + if (mmdc == MAP_FAILED) {\ + perror("Unable to map MMDC address space");\ + exit(1);\ + }\ + mmdc->madpcr1 = axi_arm1;\ + msync(&(mmdc->madpcr1),4,MS_SYNC); + +#define SETUP_LITMUS \ + unsigned int wait = 0; \ + if (be_migrate_to_domain(_rt_core) < 0) { \ + perror("Unable to migrate to specified CPU"); \ + exit(1); \ + } \ + struct reservation_config res; \ + res.id = gettid(); \ + res.cpu = cpu; \ + res.priority = LITMUS_HIGHEST_PRIORITY; \ + /* we take over half the CPU time (these are ns) */ \ + res.polling_params.budget = ms2ns(3000); \ + res.polling_params.period = ms2ns(3000); \ + res.polling_params.offset = 0; \ + res.polling_params.relative_deadline = ms2ns(3000); \ + /* Not 100% sure that we should use periodic polling */ \ + if (reservation_create(PERIODIC_POLLING, &res) < 0) { \ + perror("Unable to create reservation"); \ + exit(1); \ + } \ + struct rt_task rt_param; \ + init_rt_task_param(&rt_param); \ + /* Supposedly the next two parameters are irrelevant when reservations are enabled, but I'm leaving them anyway... */ \ + rt_param.exec_cost = ms2ns(999); \ + rt_param.period = ms2ns(1000); \ + rt_param.priority = LITMUS_HIGHEST_PRIORITY; \ + rt_param.cls = RT_CLASS_HARD; \ + rt_param.release_policy = TASK_PERIODIC; \ + rt_param.budget_policy = NO_ENFORCEMENT; \ + rt_param.cpu = cpu; \ + if (set_rt_task_param(gettid(), &rt_param) < 0) { \ + perror("Unable to set real-time parameters"); \ + exit(1); \ + } \ + if (init_litmus() != 0) { \ + perror("init_litmus failed"); \ + exit(1); \ + } \ + MC2_SETUP \ + if (task_mode(LITMUS_RT_TASK) != 0) { \ + perror("Unable to become real-time task"); \ + exit(1); \ + } \ + if (wait && wait_for_ts_release() != 0) { \ + perror("Unable to wait for taskset release"); \ + exit(1); \ + } + +#if MC2 +#define MC2_SETUP \ + struct mc2_task mc2_param; \ + mc2_param.res_id = gettid(); \ + mc2_param.crit = CRIT_LEVEL_A; \ + if (set_mc2_task_param(gettid(), &mc2_param) < 0) { \ + perror("Unable to set MC^2 task params"); \ + exit(1); \ + } \ + set_page_color(rt_param.cpu); +#else +#define MC2_SETUP +#endif + +#define CLEANUP_LITMUS \ + if (task_mode(BACKGROUND_TASK) != 0) { \ + perror("Unable to become a real-time task"); \ + exit(1); \ + } \ + reservation_destroy(gettid(), rt_param.cpu); + +#if __arm__ +// On ARM, manually flush the cache +#define FLUSH_CACHES \ + volatile uint8_t buffer[L2_SIZE * 4]; \ + for (uint32_t j = 0; j < 4; j++) \ + for (uint32_t i = 0; i < L2_SIZE * 4; i += LINE_SIZE) \ + buffer[i]++; +#else +// On x86 call the wbinvld instruction (it's in a kernel module due to it being ring-0) +#define FLUSH_CACHES \ + FILE *fp = fopen("/proc/wbinvd", "r");\ + if (fp == NULL) {\ + perror("Cache flush module interface cannot be opened");\ + exit(1);\ + }\ + char dummy;\ + if (fread(&dummy, 1, 1, fp) == 0) {\ + perror("Unable to access cache flush module interface");\ + exit(1);\ + }\ + fclose(fp); +#endif + +// This semaphore-based synchronization is from Sims +#define FIRST_UNLOCK \ + if (_rt_lock_id == 1) {\ + if (sem_post(_rt_second_sem) != 0) {\ + perror("Unable to unlock second semaphore");\ + exit(1);\ + }\ + } \ + else {\ + if (sem_post(_rt_first_sem) != 0) {\ + perror("Unable to unlock first semaphore");\ + exit(1);\ + }\ + } \ + +#define FIRST_LOCK \ + if (_rt_lock_id == 1) {\ + if (sem_wait(_rt_first_sem) != 0) {\ + perror("Unable to wait on first semaphore");\ + exit(1);\ + }\ + }\ + else {\ + if (sem_wait(_rt_second_sem) != 0) {\ + perror("Unable to wait on second semaphore");\ + exit(1);\ + }\ + } + +// This ensures a very low difference between pair member start times +#define BARRIER_SYNC \ + if (__sync_bool_compare_and_swap(_rt_barrier, 0, 1)) {\ + while (!__sync_bool_compare_and_swap(_rt_barrier, 0, 0)) {};\ + }\ + else {\ + __sync_bool_compare_and_swap(_rt_barrier, 1, 0);\ + } + +// Buffer timing result from a single job +static void _rt_save_job_result() { + if (_rt_jobs_complete >= _rt_max_jobs) { + fprintf(stderr, "Max jobs setting too small! Trying to record job #%ld when we only have space for %ld jobs. Exiting...\n", _rt_jobs_complete, _rt_max_jobs); + exit(1); + } + if (_rt_jobs_complete > -1 && _rt_will_output) { +#ifdef PAIRED + _rt_start_time[_rt_jobs_complete] = _rt_start.tv_sec; + _rt_start_time[_rt_jobs_complete] *= _BILLION; + _rt_start_time[_rt_jobs_complete] += _rt_start.tv_nsec; + _rt_end_time[_rt_jobs_complete] = _rt_end.tv_sec; + _rt_end_time[_rt_jobs_complete] *= _BILLION; + _rt_end_time[_rt_jobs_complete] += _rt_end.tv_nsec; +#else + _rt_exec_time[_rt_jobs_complete] = _rt_end.tv_sec - _rt_start.tv_sec; + _rt_exec_time[_rt_jobs_complete] *= _BILLION; + _rt_exec_time[_rt_jobs_complete] += _rt_end.tv_nsec - _rt_start.tv_nsec; +#endif /* PAIRED */ +#if MMDC_PROF + _rt_mmdc_read[_rt_jobs_complete] = mmdc_res.read_bytes; + _rt_mmdc_write[_rt_jobs_complete] = mmdc_res.write_bytes; +#endif + } +} + +// Save all buffered timing results to disk +static void _rt_write_to_file() { + char fileName[_RT_FILENAME_LEN]; + FILE *fp; + munlockall(); + if (!_rt_will_output) + goto out; + strcpy(fileName, _rt_run_id); + strcat(fileName, ".txt"); + fp = fopen(fileName, "a"); + if (fp == NULL) { + perror("Unable to open output file"); + exit(1); + } + // Baseline output uses a similar format with "none" for unused fields + for (int i = 0; i < _rt_jobs_complete; i++){ + fprintf(fp, "%s %s %u %s %ld", _rt_our_prog_name, _rt_other_prog_name, + _rt_core, _rt_other_core, _rt_max_jobs); +#ifdef PAIRED + // For unclear legacy reasons, paired tasks emit sec and ns separately + fprintf(fp, " %lld %lld %lld %lld", + _rt_start_time[i] / _BILLION, _rt_start_time[i] % _BILLION, + _rt_end_time[i] / _BILLION, _rt_end_time[i] % _BILLION); +#else + fprintf(fp, " %.f", _rt_exec_time[i]); +#endif /* PAIRED */ + fprintf(fp, " %s %d %.f %.f\n", _rt_run_id, i, +#if MMDC_PROF + _rt_mmdc_read[i], _rt_mmdc_write[i]); +#else + 0.0, 0.0); +#endif /* MMDC_PROF */ + } + fclose(fp); +out: +#if LITMUS + CLEANUP_LITMUS +#endif /* LITMUS */ +#ifdef PAIRED + munmap(_rt_barrier, 1); + shm_unlink("/_libextra_barrier"); + sem_unlink("/_libextra_first_sem"); + sem_unlink("/_libextra_second_sem"); + free(_rt_start_time); + free(_rt_end_time); +#else + free(_rt_exec_time); +#endif /* PAIRED */ +#if MMDC_PROF + free(_rt_mmdc_read); + free(_rt_mmdc_write); +#endif /* MMDC_PROF */ +} + +// Start a job +static void _rt_start_loop() { +#if LITMUS + if (sleep_next_period() != 0) { + perror("Unable to sleep for next period"); + } +#else + sched_yield(); +#endif /* LITMUS */ +#ifdef PAIRED + FIRST_UNLOCK + FIRST_LOCK +#endif /* PAIRED */ + FLUSH_CACHES +#ifdef PAIRED + BARRIER_SYNC +#endif /* PAIRED */ +#if MMDC_PROF + /* This disables profiling, resets the counters, clears the overflow bit, and enables profiling */ + start_mmdc_profiling(mmdc); +#endif /* MMDC_PROF */ + clock_gettime(CLOCK_MONOTONIC, &_rt_start); +} + +// Complete a job +static void _rt_stop_loop() { + clock_gettime(CLOCK_MONOTONIC, &_rt_end); +#if MMDC_PROF + /* This freezes the profiling and makes results available */ + pause_mmdc_profiling(mmdc); + get_mmdc_profiling_results(mmdc, &mmdc_res); +#endif /* MMDC_PROF */ + _rt_save_job_result(); + _rt_jobs_complete++; +} + +/****** New API ****** + * Intended structure: + * + * |int main(int argc, char **argv) { + * | SET_UP + * | ... + * | for_each_job { + * | tacleInit(); + * | tacleMain(); + * | } + * | WRITE_TO_FILE + * |} + * + * The main() function must call its parameters argc and argv for SET_UP to be + * able to read them. + * Only SET_UP necessarily has to be in main(). + * + * We use some niche C features, here's a quick explaination: + * 1. The && operator doesn't evaluate the right-hand side of the expression + * unless the left side evaluated to true. We use this to only execute + * _rt_start_loop() when the loop will actually run. + * 2. The comma operator executes the first expression and then throws away the + * result. We use this to call our void function from inside a comparison. + */ +#define for_each_job \ + for (; _rt_jobs_complete < _rt_max_jobs && (_rt_start_loop(),1); \ + _rt_stop_loop()) + +/****** Legacy API ****** + * Intended structure: + * + * |int main(int argc, char **argv) { + * | SET_UP + * | for (jobsComplete=0; jobsCompleteheight; + cols = in->width; + + //out = fMallocHandle(rows, cols); + + for(i=0; iheight; + cols = in->width; + + out = fMallocHandle(rows, cols); + + for(i=0; iheight; + cols = a->width; + + c = fMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + { + asubsref(c,i) = asubsref(a,i) / b; + } + + return c; +} diff --git a/SD-VBS/common/c/fFind3.c b/SD-VBS/common/c/fFind3.c new file mode 100644 index 0000000..a783bae --- /dev/null +++ b/SD-VBS/common/c/fFind3.c @@ -0,0 +1,46 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* fFind3(F2D* in) +{ + int r, k, y, x, i, j; + F2D *points; + + y = in->height; + x = in->width; + + r = 0; + for(i=0; i +#include +#include "sdvbs_common.h" + +void fFreeHandle(F2D* out) +{ + if(out != NULL) + free(out); + + return; +} + diff --git a/SD-VBS/common/c/fHorzcat.c b/SD-VBS/common/c/fHorzcat.c new file mode 100644 index 0000000..9845e7c --- /dev/null +++ b/SD-VBS/common/c/fHorzcat.c @@ -0,0 +1,40 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* fHorzcat(F2D* a, F2D* b) +{ + F2D* out_, *out, *c; + int rows=0, cols=0, i, j, k, c_1, c_2, r_3, c_3; + int r_1; + + r_1 = a->height; + c_1 = a->width; + cols += c_1; + c_2 = b->width; + cols += c_2; + rows = r_1; + + out = fMallocHandle(rows, cols); + + for(i=0; i +#include +#include "sdvbs_common.h" + +F2D* fMallocHandle(int rows, int cols) +{ + int i, j; + F2D* out; + + out = (F2D*)malloc(sizeof(F2D) + sizeof(float)*rows*cols); + out->height = rows; + out->width = cols; + return out; +} diff --git a/SD-VBS/common/c/fMdivide.c b/SD-VBS/common/c/fMdivide.c new file mode 100644 index 0000000..671e7d1 --- /dev/null +++ b/SD-VBS/common/c/fMdivide.c @@ -0,0 +1,27 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* fMdivide(F2D* a, F2D* b) +{ + F2D *c; + int i, j, rows, cols; + + rows = a->height; + cols = a->width; + + if(rows != b->height || cols != b->width) + { + printf("fMDivide Mismatch = \nrows: %d\t%d\ncols: %d\t%d\n", rows, b->height, cols, b->width); + return NULL; + } + + c = fMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) / asubsref(b,i); + + return c; +} diff --git a/SD-VBS/common/c/fMinus.c b/SD-VBS/common/c/fMinus.c new file mode 100644 index 0000000..6b4954e --- /dev/null +++ b/SD-VBS/common/c/fMinus.c @@ -0,0 +1,21 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* fMinus(F2D* a, F2D* b) +{ + F2D *c; + int i, j, rows, cols; + + rows = a->height; + cols = a->width; + + c = fMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) - asubsref(b,i); + + return c; +} diff --git a/SD-VBS/common/c/fMtimes.c b/SD-VBS/common/c/fMtimes.c new file mode 100644 index 0000000..c765d2f --- /dev/null +++ b/SD-VBS/common/c/fMtimes.c @@ -0,0 +1,38 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* fMtimes(F2D* a, F2D* b) +{ + F2D *out; + int m, p, p1, n, i, j, k; + float temp; + + m = a->height; + p = a->width; + + p1 = b->height; + n = b->width; + + out = fMallocHandle(m,n); + + for(i=0; iheight; + cols = a->width; + + c = fMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) { + asubsref(c,i) = asubsref(a,i) + asubsref(b,i); + } + return c; +} diff --git a/SD-VBS/common/c/fResetArray.c b/SD-VBS/common/c/fResetArray.c new file mode 100644 index 0000000..23a853a --- /dev/null +++ b/SD-VBS/common/c/fResetArray.c @@ -0,0 +1,19 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include "sdvbs_common.h" + +void fResetArray(F2D *out, int rows, int cols, float val) +{ + int i, j; + + for(i=0; i +#include +#include "sdvbs_common.h" + +F2D* fResetHandle(F2D* out, int rows, int cols) +{ + int i, j; + //F2D* out; + + //out = (F2D*)malloc(sizeof(F2D) + sizeof(float)*rows*cols); + out->height = rows; + out->width = cols; + //printf("fmalloc happened\n"); + return out; +} diff --git a/SD-VBS/common/c/fReshape.c b/SD-VBS/common/c/fReshape.c new file mode 100644 index 0000000..a078826 --- /dev/null +++ b/SD-VBS/common/c/fReshape.c @@ -0,0 +1,27 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* fReshape(F2D* in, int rows, int cols) +{ + F2D *out; + int i, j, k; + int r, c; + + r = in->height; + c = in->width; + + out = fMallocHandle(rows, cols); + + k = 0; + for(i=0; iheight; + cols = input->width; + + in = fCopy(input, in); + //ind = iMallocHandle(rows,cols); + + for(i=0; iheight; + c1 = in1->width; + + buffer = (float*)malloc(sizeof(float)*r1*c1); + + sprintf(file, "%s", path); + fd = fopen(file, "r"); + + if(fd == NULL) + { + printf("Error: Expected file not opened %s\n", file); + return -1; + } + + while(!feof(fd)) + { + fscanf(fd, "%f", &buffer[count]); + count++; + } + count--; + + if(count != (r1*c1)) + { + printf("Checking error: dimensions mismatch. Expected = %d, Observed = %d \n", count, (r1*c1)); + return -1; + } + + for(i=0; itol || (buffer[i]-inVal)>tol ) + { + printf("Mismatch %d: (%f, %f)\n", i, buffer[i], inVal); + return -1; + } + } + + fclose(fd); + printf("Verification\t\t- Successful\n"); + free(buffer); + return ret; +} + + diff --git a/SD-VBS/common/c/fSetArray.c b/SD-VBS/common/c/fSetArray.c new file mode 100644 index 0000000..cd8269b --- /dev/null +++ b/SD-VBS/common/c/fSetArray.c @@ -0,0 +1,22 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include "sdvbs_common.h" + +F2D* fSetArray(int rows, int cols, float val) +{ + int i, j; + F2D *out; + out = fMallocHandle(rows, cols); + + for(i=0; iheight; + cols = in->width; + + sorted = fDeepCopy(in); + + for(k=0; kheight; + cols = input->width; + + in = fDeepCopy(input); + ind = iMallocHandle(rows,cols); + + for(i=0; iheight; + cols = inMat->width; + + if(cols == 1 || rows == 1) + Rcols = 1; + else + Rcols = cols; + + outMat = fSetArray(1,Rcols,0); + + if( cols == 1) + { + temp = 0; + for( j=0; jheight; + cols = inMat->width; + + if(dir == 1) + { + newRow = 1; + newCols = cols; + } + else + { + newRow = rows; + newCols = 1; + } + + outMat = fSetArray(newRow,newCols,0); + + if(dir == 1) + { + for (i=0; iheight; + cols = a->width; + + c = fMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) * asubsref(b,i); + + return c; +} diff --git a/SD-VBS/common/c/fTranspose.c b/SD-VBS/common/c/fTranspose.c new file mode 100644 index 0000000..9611be2 --- /dev/null +++ b/SD-VBS/common/c/fTranspose.c @@ -0,0 +1,27 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* fTranspose(F2D* a) +{ + F2D *out; + int m, p, p1, n, i, j, k; + float temp; + + m = a->height; + n = a->width; + + out = fMallocHandle(n, m); + k = 0; + for(i=0; i +#include +#include "sdvbs_common.h" + +void fWriteMatrix(F2D* input, char* inpath) +{ + FILE* fp; + char im[100]; + int rows,cols, i, j; + + sprintf(im, "%s/expected_C.txt", inpath); + fp = fopen(im, "w"); + + rows = input->height; + cols = input->width; + + for(i=0; iheight; + na = a->width; + + mb = b->height; + nb = b->width; + + ci = ma; + cj = na; + + c = fSetArray(ci, cj, 0); + + r_index = mb/2; + c_index = nb/2; + + for(i=0; i=0 && ri < ma && ci >= 0 && ci < na) + subsref(c,i,j) += subsref(a,ri,ci) * subsref(b,mm,nn); + } + } + } + } + + return c; +} diff --git a/SD-VBS/common/c/ffConv2_dY.c b/SD-VBS/common/c/ffConv2_dY.c new file mode 100644 index 0000000..e480eaa --- /dev/null +++ b/SD-VBS/common/c/ffConv2_dY.c @@ -0,0 +1,52 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* ffConv2_dY(F2D* a, F2D* b) +{ + F2D *c, *out; + int ma, na, mb, nb, ci, cj, i, j, m, n; + int r_index, c_index; + + ma = a->height; + na = a->width; + + mb = b->height; + nb = b->width; + + r_index = ceil((mb + 1.0)/2.0); + c_index = ceil((nb + 1.0)/2.0); + + ci = ma+mb-1; + cj = na+nb-1; + + c = fSetArray(ci, cj, 0); + + for(i=0; i=0 && (j-n)>=0 && (i-m)height; + cols = a->width; + + c = fMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) / asubsref(b,i); + + return c; +} diff --git a/SD-VBS/common/c/ffTimes.c b/SD-VBS/common/c/ffTimes.c new file mode 100644 index 0000000..8ef84b8 --- /dev/null +++ b/SD-VBS/common/c/ffTimes.c @@ -0,0 +1,21 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* ffTimes(F2D* a, float b) +{ + F2D *c; + int i, j, rows, cols; + + rows = a->height; + cols = a->width; + + c = fMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) * b; + + return c; +} diff --git a/SD-VBS/common/c/ffVertcat.c b/SD-VBS/common/c/ffVertcat.c new file mode 100644 index 0000000..00fa74b --- /dev/null +++ b/SD-VBS/common/c/ffVertcat.c @@ -0,0 +1,36 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* ffVertcat(F2D* matrix1, F2D* matrix2) +{ + F2D *outMatrix; + int row1, col1, row2, col2, i, j, k; + + row1 = matrix1->height; + col1 = matrix1->width; + + row2 = matrix2->height; + col2 = matrix2->width; + + outMatrix = fMallocHandle(row1+row2, col1); + + for( i=0; iheight; + na = a->width; + + mb = b->height; + nb = b->width; + + r_index = ceil((mb + 1.0)/2.0); + c_index = ceil((nb + 1.0)/2.0); + + ci = ma+mb-1; + cj = na+nb-1; + + c = fSetArray(ci, cj, 0); + + for(i=0; i=0 && (j-n)>=0 && (i-m)height; + na = a->width; + + mb = b->height; + nb = b->width; + + r_index = ceil((mb + 1.0)/2.0); + c_index = ceil((nb + 1.0)/2.0); + + ci = ma+mb-1; + cj = na+nb-1; + + c = fSetArray(ci, cj, 0); + + for(i=0; i=0 && (j-n)>=0 && (i-m)height; + cols = in->width; + + //out = fMallocHandle(rows, cols); + + for(i=0; iheight; + cols = in->width; + + out = fMallocHandle(rows, cols); + + for(i=0; iwidth; + cols += c_1; + + c_2 = b->width; + cols += c_2; + + r_3 = c->height; + c_3 = c->width; + cols += c_3; + rows = r_3; + + out = fMallocHandle(rows, cols); + + for(i=0; iwidth != in2 -> width || in1->height != in2->height) return 0; + for(int i = 0; i < in1->width;i++){ + for(int j = 0; j < in1->height;j++){ + if(subsref(in1,i,j) != subsref(in2,i,j)) return 0; + } + } + return 1; + +} + + diff --git a/SD-VBS/common/c/iDeepCopy.c b/SD-VBS/common/c/iDeepCopy.c new file mode 100644 index 0000000..8d56680 --- /dev/null +++ b/SD-VBS/common/c/iDeepCopy.c @@ -0,0 +1,23 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +I2D* iDeepCopy(I2D* in) +{ + int i, j; + I2D* out; + int rows, cols; + + rows = in->height; + cols = in->width; + + out = iMallocHandle(rows, cols); + + for(i=0; i +#include +#include "sdvbs_common.h" + +void iFreeHandle(I2D* out) +{ + if(out != NULL) + free(out); + + return; +} + diff --git a/SD-VBS/common/c/iHorzcat.c b/SD-VBS/common/c/iHorzcat.c new file mode 100644 index 0000000..ac1c4ea --- /dev/null +++ b/SD-VBS/common/c/iHorzcat.c @@ -0,0 +1,41 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +I2D* iHorzcat(I2D* a, I2D* b) +{ + I2D *out, *c; + int rows=0, cols=0, i, j, k, c_1, c_2, r_3, c_3; + int r_1; + + r_1 = a->height; + c_1 = a->width; + cols += c_1; + + c_2 = b->width; + cols += c_2; + rows = r_1; + + out = iMallocHandle(rows, cols); + + for(i=0; i +#include +#include "sdvbs_common.h" + +I2D* iMallocHandle(int rows, int cols) +{ + int i, j; + I2D* out; + + out = (I2D*)malloc(sizeof(I2D) + sizeof(int)*rows*cols); + out->height = rows; + out->width = cols; + //printf("imalloc happened\n"); + return out; +} + diff --git a/SD-VBS/common/c/iMinus.c b/SD-VBS/common/c/iMinus.c new file mode 100644 index 0000000..a0ed908 --- /dev/null +++ b/SD-VBS/common/c/iMinus.c @@ -0,0 +1,21 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +I2D* iMinus(I2D* a, I2D* b) +{ + I2D *c; + int i, j, rows, cols; + + rows = a->height; + cols = a->width; + + c = iMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) - asubsref(b,i); + + return c; +} diff --git a/SD-VBS/common/c/iResetArray.c b/SD-VBS/common/c/iResetArray.c new file mode 100644 index 0000000..3659d15 --- /dev/null +++ b/SD-VBS/common/c/iResetArray.c @@ -0,0 +1,22 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include "sdvbs_common.h" + +void iResetArray(I2D* out, int rows, int cols, int val) +{ + int i, j; + //I2D *out; + //out = iMallocHandle(rows, cols); + + for(i=0; iheight; + c = in->width; + + out = iMallocHandle(rows, cols); + + k = 0; + for(i=0; i +#include +#include "sdvbs_common.h" + +I2D* iSetArray(int rows, int cols, int val) +{ + int i, j; + I2D *out; + out = iMallocHandle(rows, cols); + + for(i=0; iheight; + cols = in->width; + + sorted = iDeepCopy(in); + + for(k=0; kheight; + cols = in->width; + + sorted = iDeepCopy(in); + ind = iMallocHandle(rows, cols); + + for(i=0; iheight; + cols = a->width; + + c = iMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) * asubsref(b,i); + + return c; +} + diff --git a/SD-VBS/common/c/iTranspose.c b/SD-VBS/common/c/iTranspose.c new file mode 100644 index 0000000..79b65d2 --- /dev/null +++ b/SD-VBS/common/c/iTranspose.c @@ -0,0 +1,26 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +I2D* iTranspose(I2D* a) +{ + I2D *out; + int m, p, p1, n, i, j, k; + int temp; + + m = a->height; + n = a->width; + + out = iMallocHandle(n, m); + k = 0; + for(i=0; iheight; + col1 = matrix1->width; + + row2 = matrix2->height; + col2 = matrix2->width; + + outMatrix = iMallocHandle(row1+row2, col1); + + for( i=0; i +#include "sdvbs_common.h" + +I2D* ifDeepCopy(F2D* in) +{ + int i, j; + I2D *out; + int rows, cols; + + rows = in->height; + cols = in->width; + + out = iMallocHandle(rows, cols); + + for(i=0; iheight; + p = a->width; + + p1 = b->height; + n = b->width; + + out = fMallocHandle(m,n); + + for(i=0; iheight; + na = a->width; + + mb = b->height; + nb = b->width; + + r_index = ceil((mb + 1.0)/2.0); + c_index = ceil((nb + 1.0)/2.0); + + ci = ma+mb-1; + cj = na+nb-1; + + c = iSetArray(ci, cj, 0); + + for(i=0; i=0 && (j-n)>=0 && (i-m)height; + cols = imageIn->width; + + imageOut = fSetArray(rows, cols, 0); + tempOut = fSetArray(rows, cols, 0); + kernel = iMallocHandle(1, 5); + + asubsref(kernel,0) = 1; + asubsref(kernel,1) = 4; + asubsref(kernel,2) = 6; + asubsref(kernel,3) = 4; + asubsref(kernel,4) = 1; + kernelSize = 5; + kernelSum = 16; + + startCol = 2; + endCol = cols - 2; + halfKernel = 2; + + startRow = 2; + endRow = rows - 2; + + for(i=startRow; iheight; + cols = imageIn->width; + + fResetArray(imageOut, rows, cols, 0); + fResetArray(tempOut, rows, cols, 0); + //kernel = iMallocHandle(1, 5); + + asubsref(kernel,0) = 1; + asubsref(kernel,1) = 4; + asubsref(kernel,2) = 6; + asubsref(kernel,3) = 4; + asubsref(kernel,4) = 1; + kernelSize = 5; + kernelSum = 16; + + startCol = 2; + endCol = cols - 2; + halfKernel = 2; + + startRow = 2; + endRow = rows - 2; + + for(i=startRow; iheight; + cols = imageIn->width; + + // level 1 is the base image. + + outputRows = floor((rows+1)/2); + outputCols = floor((cols+1)/2); + + temp = fSetArray(rows, outputCols, 0); + imageOut = fSetArray(outputRows, outputCols, 0); + kernel = iMallocHandle(1, 5); + + asubsref(kernel,0) = 1; + asubsref(kernel,1) = 4; + asubsref(kernel,2) = 6; + asubsref(kernel,3) = 4; + asubsref(kernel,4) = 1; + kernelSize = 5; + kernelSum = 16; + + startCol = 2; + endCol = cols - 2; + halfKernel = 2; + + startRow = 2; + endRow = rows - 2; + + for(i=startRow; i +#include +#include "sdvbs_common.h" + +I2D* isMinus(I2D* a, int b) +{ + I2D *c; + int i, j, rows, cols; + + rows = a->height; + cols = a->width; + + c = iMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) - b; + + return c; +} diff --git a/SD-VBS/common/c/isPlus.c b/SD-VBS/common/c/isPlus.c new file mode 100644 index 0000000..9c7438f --- /dev/null +++ b/SD-VBS/common/c/isPlus.c @@ -0,0 +1,22 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +I2D* isPlus(I2D* a, int b) +{ + I2D *c; + int i, j, rows, cols; + + rows = a->height; + cols = a->width; + + c = iMallocHandle(rows, cols); + + for(i=0; i<(rows*cols); i++) + asubsref(c,i) = asubsref(a,i) + b; + + return c; +} + diff --git a/SD-VBS/common/c/photonEndTiming.c b/SD-VBS/common/c/photonEndTiming.c new file mode 100644 index 0000000..b15c4de --- /dev/null +++ b/SD-VBS/common/c/photonEndTiming.c @@ -0,0 +1,22 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +/** C File **/ +#include +#include +#include +#include +#include +#include "timingUtils.h" +#include "sdvbs_common.h" + +unsigned int * photonEndTiming() +{ + static unsigned int *array; + array = (unsigned int*)malloc(sizeof(unsigned int)*2); + + magic_timing_begin(array[0], array[1]); + return array; +} + diff --git a/SD-VBS/common/c/photonPrintTiming.c b/SD-VBS/common/c/photonPrintTiming.c new file mode 100644 index 0000000..06df530 --- /dev/null +++ b/SD-VBS/common/c/photonPrintTiming.c @@ -0,0 +1,22 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +/** C File **/ +#include +#include +#include +#include +#include +#include "timingUtils.h" +#include "sdvbs_common.h" + +void photonPrintTiming(unsigned int * elapsed) +{ + if(elapsed[1] == 0) + printf("Cycles elapsed\t\t- %u\n\n", elapsed[0]); + else + printf("Cycles elapsed\t\t- %u%u\n\n", elapsed[1], elapsed[0]); +} + +/** End of C Code **/ diff --git a/SD-VBS/common/c/photonReportTiming.c b/SD-VBS/common/c/photonReportTiming.c new file mode 100644 index 0000000..c41d103 --- /dev/null +++ b/SD-VBS/common/c/photonReportTiming.c @@ -0,0 +1,28 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +/** C File **/ +#include +#include +#include +#include +#include +#include "timingUtils.h" +#include "sdvbs_common.h" + +unsigned int * photonReportTiming(unsigned int* startCycles,unsigned int* endCycles) +{ + + static unsigned int *elapsed; + elapsed = (unsigned int*)malloc(sizeof(unsigned int)*2); + unsigned long long start = (((unsigned long long)0x0) | startCycles[0]) << 32 | startCycles[1]; + unsigned long long end = (((unsigned long long)0x0) | endCycles[0]) << 32 | endCycles[1]; + unsigned long long diff = end - start; + elapsed[0] = (unsigned int)(diff >> 32); + elapsed[1] = (unsigned int)(diff & 0xffffffff); + return elapsed; + +} + +/** End of C Code **/ diff --git a/SD-VBS/common/c/photonStartTiming.c b/SD-VBS/common/c/photonStartTiming.c new file mode 100644 index 0000000..0d0b2b1 --- /dev/null +++ b/SD-VBS/common/c/photonStartTiming.c @@ -0,0 +1,23 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +/** C File **/ +#include +#include +#include +#include +#include +#include "timingUtils.h" +#include "sdvbs_common.h" + +unsigned int* photonStartTiming() +{ + static unsigned int *array; + + array = (unsigned int*)malloc(sizeof(unsigned int)*2); + magic_timing_begin(array[0], array[1]); + return array; +} + +/** End of C Code **/ diff --git a/SD-VBS/common/c/randWrapper.c b/SD-VBS/common/c/randWrapper.c new file mode 100644 index 0000000..cadcc32 --- /dev/null +++ b/SD-VBS/common/c/randWrapper.c @@ -0,0 +1,30 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include "sdvbs_common.h" + +F2D* randWrapper(int m, int n) +{ + F2D *out; + float seed; + int i,j; + + out = fSetArray(m, n, 0); + seed = 0.9; + + for(i=0; i +F2D* randnWrapper(int m, int n) +{ + F2D *out; + float seed; + int i,j; + + out = fSetArray(m, n, 0); + seed = 0.9; + + for(i=0; i +#include +#include "sdvbs_common.h" + +I2D* readImage(const char* pathName) +{ + // Reading BMP image + char signature[2]; + int file_size; + short int reserved1; + short int reserved2; + int loc_of_bitmap; + + int size_of_infoheader; + int width; + int height; + short int number_of_planes; + short int bits_per_pixel; + + int compression_method; + int bytes_of_bitmap; + int hori_reso; + int vert_reso; + int no_of_colors; + int no_of_imp_colors; + + int nI,nJ; + int pixSize; + + unsigned char tempb,tempg,tempr,tempjunk[12]; + int ta; + I2D* srcImage; + + FILE *input; + input = fopen(pathName,"rb"); + if(input == NULL) + { + perror("File pointer error"); + return NULL; + } + else + { + //start of header information + fread(&signature,sizeof(signature),1,input); + fread(&file_size,sizeof(file_size),1,input); + fread(&reserved1,sizeof(reserved1),1,input); + fread(&reserved2,sizeof(reserved2),1,input); + fread(&loc_of_bitmap,sizeof(loc_of_bitmap),1,input); + + fread(&size_of_infoheader,sizeof(size_of_infoheader),1,input); + fread(&width,sizeof(width),1,input); // Reads the width of the image + fread(&height,sizeof(height),1,input); // Reads the height of the image + fread(&number_of_planes,sizeof(number_of_planes),1,input); + fread(&bits_per_pixel,sizeof(bits_per_pixel),1,input); + fread(&compression_method,sizeof(compression_method),1,input); + fread(&bytes_of_bitmap,sizeof(bytes_of_bitmap),1,input); + + fread(&hori_reso,sizeof(hori_reso),1,input); + fread(&vert_reso,sizeof(vert_reso),1,input); + fread(&no_of_colors,sizeof(no_of_colors),1,input); + fread(&no_of_imp_colors,sizeof(no_of_imp_colors),1,input); + //end of header information + + srcImage = iMallocHandle(height, width); + + // Conditions to check whether the BMP is interleaved and handling few exceptions + if(srcImage->height <= 0 || srcImage->width <= 0 || signature[0] != 'B' || signature[1] != 'M' || ( bits_per_pixel !=24 && bits_per_pixel !=8 ) ) + { + printf("ERROR in BMP read: The input file is not in standard BMP format"); + return NULL; + } + fseek(input,loc_of_bitmap,SEEK_SET); + + if (bits_per_pixel == 8) + { + for(nI = (height - 1); nI >= 0 ; nI--) + { + for(nJ = 0;nJ < width; nJ++) + { + fread(&tempg,sizeof(unsigned char),1,input); + subsref(srcImage,nI,nJ) = (int)tempg; + } + } + } + else if (bits_per_pixel == 24) + { + for(nI = (height - 1); nI >= 0 ; nI--) + { + for(nJ = 0;nJ < width; nJ++) + { + fread(&tempb,sizeof(unsigned char),1,input); + fread(&tempg,sizeof(unsigned char),1,input); + fread(&tempr,sizeof(unsigned char),1,input); + ta = (3*tempr + 6*tempg + tempb)/10; + ta = tempg; + subsref(srcImage,nI,nJ) = (int)ta; + } + } + } + else + { + return NULL; + } + + fclose(input); + return srcImage; + } +} diff --git a/SD-VBS/common/c/sdvbs_common.h b/SD-VBS/common/c/sdvbs_common.h new file mode 100644 index 0000000..14e28b2 --- /dev/null +++ b/SD-VBS/common/c/sdvbs_common.h @@ -0,0 +1,139 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#ifndef _SDVBS_COMMON_ +#define _SDVBS_COMMON_ + +#include +#include +#include + +typedef struct +{ + int width; + int height; + int data[]; +}I2D; + +typedef struct +{ + int width; + int height; + unsigned int data[]; +}UI2D; + +typedef struct +{ + int width; + int height; + float data[]; +}F2D; + +#define subsref(a,i,j) a->data[(i) * a->width + (j)] +#define asubsref(a,i) a->data[i] +#define arrayref(a,i) a[i] + +/** Image read and write **/ +I2D* readImage(const char* pathName);; +F2D* readFile(unsigned char* fileName); + + +/** Memory allocation functions **/ +I2D* iMallocHandle(int rows, int cols); +F2D* fMallocHandle(int rows, int cols); +F2D* fResetHandle(F2D* out, int rows, int cols); +UI2D* uiMallocHandle(int rows, int cols); + +void iFreeHandle(I2D* out); +void fFreeHandle(F2D* out); +void uiFreeHandle(UI2D* out); + +/** Memory copy/set function **/ +I2D* iSetArray(int rows, int cols, int val); +void iResetArray(I2D* out, int rows, int cols, int val); +F2D* fSetArray(int rows, int cols, float val); +void fResetArray(F2D* out, int rows, int cols, float val); +I2D* iDeepCopy(I2D* in); +F2D* fDeepCopy(F2D* in); +F2D* fCopy(F2D* in, F2D* out); +I2D* iDeepCopyRange(I2D* in, int startRow, int numberRows, int startCol, int numberCols); +F2D* fDeepCopyRange(F2D* in, int startRow, int numberRows, int startCol, int numberCols); +F2D* fiDeepCopy(I2D* in); +void fiCopy(F2D* out, I2D* in); +I2D* ifDeepCopy(F2D* in); + + +/** Matrix operations - concatenation, reshape **/ +F2D* ffVertcat(F2D* matrix1, F2D* matrix2); +I2D* iVertcat(I2D* matrix1, I2D* matrix2); +F2D* fHorzcat(F2D* a, F2D* b); +I2D* iHorzcat(I2D* a, I2D* b); +F2D* horzcat(F2D* a, F2D* b, F2D* c); +F2D* fTranspose(F2D* a); +I2D* iTranspose(I2D* a); +F2D* fReshape(F2D* in, int rows, int cols); +I2D* iReshape(I2D* in, int rows, int cols); + + +/** Binary Operations **/ +F2D* fDivide(F2D* a, float b); +F2D* fMdivide(F2D* a, F2D* b); +F2D* ffDivide(F2D* a, F2D* b); +F2D* ffTimes(F2D* a, float b); +F2D* fTimes(F2D* a, F2D* b); +I2D* iTimes(I2D* a, I2D* b); +F2D* fMtimes(F2D* a, F2D* b); +F2D* ifMtimes(I2D* a, F2D* b); +F2D* fMinus(F2D* a, F2D* b); +I2D* iMinus(I2D* a, I2D* b); +I2D* isMinus(I2D* a, int b); +F2D* fPlus(F2D* a, F2D* b); +I2D* isPlus(I2D* a, int b); + + +/** Filtering operations **/ +F2D* calcSobel_dX(F2D* imageIn); +F2D* calcSobel_dY(F2D* imageIn); +F2D* ffConv2(F2D* a, F2D* b); +F2D* fiConv2(I2D* a, F2D* b); +F2D* ffConv2_dY(F2D* a, F2D* b); +F2D* ffiConv2(F2D* a, I2D* b); +I2D* iiConv2(I2D* a, I2D* b); + + +/** Image Transformations - resize, integration etc **/ +F2D* imageResize(F2D* imageIn); +F2D* imageBlur(I2D* imageIn); +F2D* imageReblur(I2D* imageIn, F2D* imageOut, F2D* tempOut, I2D* kernel); + + +/** Support functions **/ +F2D* fFind3(F2D* in); +F2D* fSum2(F2D* inMat, int dir); +F2D* fSum(F2D* inMat); +I2D* iSort(I2D* in, int dim); +F2D* fSort(F2D* in, int dim); +I2D* iSortIndices(I2D* in, int dim); +I2D* fSortIndices(F2D* input, int dim); +I2D* fResortIndices(F2D* input, int dim, F2D* in, I2D* ind); +F2D* randnWrapper(int m, int n); +F2D* randWrapper(int m, int n); + + +/** Checking functions **/ +int selfCheck(I2D* in1, char* path, int tol); +int fSelfCheck(F2D* in1, char* path, float tol); +void writeMatrix(I2D* input, char* inpath); +void fWriteMatrix(F2D* input, char* inpath); +int iCheck(I2D* in1, I2D* in2); + +/** Timing functions **/ +unsigned int* photonEndTiming(); +unsigned int* photonStartTiming(); +unsigned int* photonReportTiming(unsigned int* startCycles,unsigned int* endCycles); +void photonPrintTiming(unsigned int * elapsed); + + +#endif + diff --git a/SD-VBS/common/c/selfCheck.c b/SD-VBS/common/c/selfCheck.c new file mode 100644 index 0000000..e79a6a4 --- /dev/null +++ b/SD-VBS/common/c/selfCheck.c @@ -0,0 +1,65 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include +#include "sdvbs_common.h" + +int selfCheck(I2D* in1, char* path, int tol) +{ + int r1, c1, ret=1; + FILE* fd; + int count=0, *buffer, i, j; + char file[100]; + int* data = in1->data; + + r1 = in1->height; + c1 = in1->width; + + buffer = (int*)malloc(sizeof(int)*r1*c1); + + sprintf(file, "%s", path); + fd = fopen(file, "r"); + if(fd == NULL) + { + printf("Error: Expected file not opened \n"); + return -1; + } + + while(!feof(fd)) + { + fscanf(fd, "%d", &buffer[count]); + count++; + } + count--; + + if(count < (r1*c1)) + { + printf("Checking error: dimensions mismatch. Expected = %d, Observed = %d \n", count, (r1*c1)); + return -1; + } + + for(i=0; itol || (abs(buffer[i])-abs(data[i]))>tol) + { + printf("Checking error: Values mismtach at %d element\n", i); + printf("Expected value = %d, observed = %d\n", buffer[i], data[i] ); + return -1; + } + } + + fclose(fd); + free(buffer); + printf("Verification\t\t- Successful\n"); + return ret; +} + + + + + + + diff --git a/SD-VBS/common/c/timingUtils.h b/SD-VBS/common/c/timingUtils.h new file mode 100644 index 0000000..818728f --- /dev/null +++ b/SD-VBS/common/c/timingUtils.h @@ -0,0 +1,99 @@ +#ifdef GCC +#define magic_timing_begin(cycleLo, cycleHi) {\ + asm volatile( "rdtsc": "=a" (cycleLo), "=d" (cycleHi)); \ +}\ + +#define magic_timing_end(cycleLo, cycleHi) {\ + unsigned tempCycleLo, tempCycleHi; \ + asm volatile( "rdtsc": "=a" (tempCycleLo), "=d" (tempCycleHi)); \ + cycleLo = tempCycleLo-cycleLo;\ + cycleHi = tempCycleHi - cycleHi;\ +}\ + + + +#define magic_timing_report(cycleLo, cycleHi) {\ + printf("Timing report: %d %d\n", cycleLo, cycleHi); \ +}\ + + + + +#endif + +#ifdef METRO + +#define magic_timing_begin(cycleLo, cycleHi) {\ + asm volatile( "mfsr $8, CYCLE_LO\n\t" \ + "mfsr $9, CYCLE_HI\n\t" \ + "addu %0, $8, $0\n\t" \ + "addu %1, $9, $0\n\t" \ + :"=r" (cycleLo), "=r" (cycleHi) \ + : \ + :"$8", "$9"\ + );\ +} + +#define magic_timing_end(cycleLo, cycleHi) {\ + asm volatile( \ + "mfsr $8, CYCLE_LO\n\t" \ + "mfsr $9, CYCLE_HI\n\t" \ + "subu %0, $8, %0\n\t" \ + "subu %1, $9, %1\n\t" \ + :"=r" (cycleLo), "=r" (cycleHi) \ + : \ + :"$8", "$9"\ + ); \ +} + +#define magic_timing_report(cycleLo, cycleHi) {\ + asm volatile( "addu $8, %0, $0\n\t" \ + "mtsr PASS $8\n\t" \ + "mtsr PASS $9\n\t" \ + : \ + :"r" (cycleLo), "r" (cycleHi) \ + : "$8", "$9" \ + );\ +} + +//#define metro_magic_timing_report(cycleLo, cycleHi) {\ +// asm volatile( "nop\n\t");\ +//} + +#endif + +#ifdef BTL + +#include "/u/kvs/raw/rawlib/archlib/include/raw.h" + +#define magic_timing_begin(cycleLo, cycleHi) {\ + raw_magic_timing_report_begin();\ +} + +#define magic_timing_end(cycleLo, cycleHi) {\ + raw_magic_timing_report_end(); \ +} + +#define magic_timing_report(cycleLo, cycleHi) {\ + raw_magic_timing_report_print(); \ +} + + +// +//void metro_magic_timing_begin(int cycleLo, int cycleHi) +//{ +// raw_magic_timing_report_begin(); +//} +// +//void metro_magic_timing_end(int cycleLo, int cycleHi) +//{ +// raw_magic_timing_report_end(); +//} +// +//void metro_magic_timing_report(int cycleLo, int cycleHi) +//{ +// raw_magic_timing_report_print(); +// return; +//} + +#endif diff --git a/SD-VBS/common/c/uiFreeHandle.c b/SD-VBS/common/c/uiFreeHandle.c new file mode 100644 index 0000000..ce64ad9 --- /dev/null +++ b/SD-VBS/common/c/uiFreeHandle.c @@ -0,0 +1,15 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include "sdvbs_common.h" + +void uiFreeHandle(UI2D* out) +{ + free(out); + + return; +} + diff --git a/SD-VBS/common/c/uiMallocHandle.c b/SD-VBS/common/c/uiMallocHandle.c new file mode 100644 index 0000000..ee26d4c --- /dev/null +++ b/SD-VBS/common/c/uiMallocHandle.c @@ -0,0 +1,20 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include "sdvbs_common.h" + +UI2D* uiMallocHandle(int rows, int cols) +{ + int i, j; + UI2D* out; + + out = malloc(sizeof(UI2D) + sizeof(unsigned int)*rows*cols); + out->height = rows; + out->width = cols; + printf("uimalloc here\n"); + return out; +} + diff --git a/SD-VBS/common/c/uiResetArray.c b/SD-VBS/common/c/uiResetArray.c new file mode 100644 index 0000000..249e570 --- /dev/null +++ b/SD-VBS/common/c/uiResetArray.c @@ -0,0 +1,19 @@ +/******************************** +Author: Sravanthi Kota Venkata +********************************/ + +#include +#include +#include "sdvbs_common.h" + +void uiResetArray(UI2D* out, int rows, int cols, int val) +{ + int i, j; + + + for(i=0; i +#include +#include "sdvbs_common.h" + +UI2D* uiSetArray(int rows, int cols, int val) +{ + int i, j; + UI2D *out; + out = uiMallocHandle(rows, cols); + + for(i=0; i +#include +#include "sdvbs_common.h" + +void writeMatrix(I2D* input, char* inpath) +{ + FILE* fp; + char im[100]; + int rows,cols, i, j; + + sprintf(im, "%s/expected_C.txt", inpath); + fp = fopen(im, "w"); + + rows = input->height; + cols = input->width; + + for(i=0; i1 input images =. + +# Variables exported from the benchmark specific Makefiles: +# BMARK +# INPUT - sqcif/qcif/cif + +# Matlab source, data and result directory + +M_DIR=$(BMARK_DIR)/src/matlab +M_DATA=$(BMARK_DIR)/data/$(INPUT) +M_RESULT=$(BMARK_DIR)/result + +# C source, data and result directory + +C_DIR=$(BMARK_DIR)/src/c +DATA_DIR=$(BMARK_DIR)/data/$(INPUT) +C_RESULT=$(M_RESULT) + +# Source files for C and Common folders + +C_SRC := $(wildcard $(C_DIR)/*.c) +COMMON_SRC := $(wildcard $(COMMON_DIR)/*.c) + +# RULES + +EXE = +INCLUDES = -I$(COMMON_DIR) -I$(C_DIR) +COMPILE_C = $(CC) $(CFLAGS) -lm -O2 $(INCLUDES) +#COMPILE_C = $(CC) $(CFLAGS) -DGENERATE_OUTPUT -lm -O2 $(INCLUDES) +COMPILE_G = $(CC) $(CFLAGS) -g -lm $(INCLUDES) +COMPILE_PG = $(COMPILE_G) -pg + +preload-run: compile + @echo preloaded timing + @echo 3 | tee /proc/sys/vm/drop_caches + @find ./ -iname "*.bmp" -exec sh -c '$(TOOL_DIR)/preload {} &' \; + @find ./ -iname "*.txt" -exec sh -c '$(TOOL_DIR)/preload {} &' \; + mkdir -p $(PRELOAD_TIMES_DIR) + @echo -e "Data set\t\t- $(INPUT)" + (time ./$(BMARK)$(EXE) $(DATA_DIR)) |& tee $(PRELOAD_TIMES_DIR)/T_$(INPUT).txt + @kill -2 `pgrep preload` + +time-run: compile + mkdir -p $(TIMES_DIR) + @echo -e "Data set\t\t- $(INPUT)" + @echo 3 | tee /proc/sys/vm/drop_caches + (time ./$(BMARK)$(EXE) $(DATA_DIR)) |& tee $(TIMES_DIR)/T_$(INPUT).txt + +c-run: compile + @echo -e "Data set\t\t- $(INPUT)" + mkdir -p $(TIMING_DIR) + @./$(BMARK)$(EXE) $(DATA_DIR) $(C_RESULT) | tee $(TIMING_DIR)/C_$(INPUT).txt + +run: compile + @echo -e "Data set\t\t- $(INPUT)" + @./$(BMARK)$(EXE) $(DATA_DIR) $(C_RESULT) + +rt-run: compile + @echo -e "Data set\t\t- $(INPUT)" + @./$(BMARK)$(EXE) $(BMARK)-$(INPUT) 5 0 12345 1 + +debug: + @echo Running Debug C Version of the benchmark + @$(COMPILE_G) $(COMMON_SRC) $(C_SRC) -o $(BMARK)$(EXE) + @valgrind --leak-check=full ./$(BMARK)$(EXE) $(DATA_DIR) $(C_RESULT) + #@gdb ./$(BMARK)$(EXE) $(DATA_DIR) $(C_RESULT) + +profile: compile-prof + @echo -e "Data set\t\t- $(INPUT)" + @./$(BMARK)$(EXE) $(DATA_DIR) $(C_RESULT) + @gprof $(BMARK)$(EXE) + +compile-preload: + @$(COMPILE_C) $(TOOL_DIR)\preload.c -o $(TOOL_DIR)\preload + +compile: $(C_SRC) + @echo + @echo -e "Benchmark\t\t- $(BMARK)" + @$(COMPILE_C) $(COMMON_SRC) $(C_SRC) -lrt -lm -w -o $(BMARK)$(EXE) + +compile-prof: $(C_SRC) + @echo + @echo -e "Benchmark\t\t- $(BMARK)" + @$(COMPILE_PG) $(COMMON_SRC) $(C_SRC) -o $(BMARK)$(EXE) + +matlab-run: + @echo + @echo -e "Benchmark\t\t- $(BMARK)" + @echo -e "Data set\t\t- $(INPUT)" + @cd $(M_DIR); $(MATLAB_PATH) -glnx86 -nosplash -nodisplay -r "script_run_profile('$(M_DATA)', '$(M_RESULT)', '$(INPUT)', '$(M_COMMON)', '$(M_TOOLBOX)'); quit" | tee $(MTIMING_DIR)/Matlab_$(INPUT).txt + +mcc-run: + @echo Generating a C standalone application + cd $(M_DIR); $(MATLAB_PATH) -nosplash -nodesktop -r "mcc -m -v script_run_profile -d $(M_RESULT); quit" + +all: c-run matlab-run mcc-run + +clean: + @-rm $(BMARK) + + + + diff --git a/SD-VBS/common/makefiles/Makefile.include b/SD-VBS/common/makefiles/Makefile.include new file mode 100644 index 0000000..f1000b9 --- /dev/null +++ b/SD-VBS/common/makefiles/Makefile.include @@ -0,0 +1,16 @@ +find-dir-with = $(shell /usr/bin/perl -e 'chomp($$_ = `pwd`); while ($$_ ne "" && ! -e "$$_/$(1)") { m:(.*)/[^/]+/??:; $$_ = $$1; } print;') + +# define canonical directories in starsearch +ifndef TOP_DIR + export TOP_DIR := $(call find-dir-with,.SD-VBS) +endif + +export MAKEFILE_COMMON_DIR=$(TOP_DIR)/common/makefiles + +# backward compatibility + +ifeq ($(TOP_DIR),) +$(error file .SD-VBS not found -- try running 'gmake setup' at the top of your source tree) +endif + + diff --git a/SD-VBS/common/makefiles/Makefile.recurse b/SD-VBS/common/makefiles/Makefile.recurse new file mode 100644 index 0000000..9a9d088 --- /dev/null +++ b/SD-VBS/common/makefiles/Makefile.recurse @@ -0,0 +1,39 @@ +# This file is included in the makefiles of the non-leaf nodes +# Thus the various targets in this file have to trickle down +# into the subdirectories +# List the subdirectories and call the target for each one of them + +################################################################ +# RECURSE +################################################################ +# Listing the subdirectories +SUBDIRS = $(patsubst %/Makefile,%,$(wildcard */Makefile)) + +RECURSE-DEPENDS = $(patsubst %,%.traverse,$(SUBDIRS)) + +all: recurse + +debug: recurse + +clean: recurse + +compile: recurse + +c-run: recurse + +matlab-run: recurse + +mcc-run: recurse + +recurse: $(RECURSE-DEPENDS) + +time-run: recurse + +preload-run: recurse + +run: recurse + +# MAKECMDGOALS contains the gmake target specified on the command line +# it is defined automatically by gmake +%.traverse: + $(MAKE) -C $* $(MAKECMDGOALS) diff --git a/SD-VBS/common/matlab/cycle.h b/SD-VBS/common/matlab/cycle.h new file mode 100644 index 0000000..2652a04 --- /dev/null +++ b/SD-VBS/common/matlab/cycle.h @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2003, 2007-8 Matteo Frigo + * Copyright (c) 2003, 2007-8 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + + +/* machine-dependent cycle counters code. Needs to be inlined. */ + +/***************************************************************************/ +/* To use the cycle counters in your code, simply #include "cycle.h" (this + file), and then use the functions/macros: + + ticks getticks(void); + + ticks is an opaque typedef defined below, representing the current time. + You extract the elapsed time between two calls to gettick() via: + + double elapsed(ticks t1, ticks t0); + + which returns a double-precision variable in arbitrary units. You + are not expected to convert this into human units like seconds; it + is intended only for *comparisons* of time intervals. + + (In order to use some of the OS-dependent timer routines like + Solaris' gethrtime, you need to paste the autoconf snippet below + into your configure.ac file and #include "config.h" before cycle.h, + or define the relevant macros manually if you are not using autoconf.) +*/ + +/***************************************************************************/ +/* This file uses macros like HAVE_GETHRTIME that are assumed to be + defined according to whether the corresponding function/type/header + is available on your system. The necessary macros are most + conveniently defined if you are using GNU autoconf, via the tests: + + dnl --------------------------------------------------------------------- + + AC_C_INLINE + AC_HEADER_TIME + AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h]) + + AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t is defined in ])],,[#if HAVE_SYS_TIME_H +#include +#endif]) + + AC_CHECK_FUNCS([gethrtime read_real_time time_base_to_time clock_gettime mach_absolute_time]) + + dnl Cray UNICOS _rtc() (real-time clock) intrinsic + AC_MSG_CHECKING([for _rtc intrinsic]) + rtc_ok=yes + AC_TRY_LINK([#ifdef HAVE_INTRINSICS_H +#include +#endif], [_rtc()], [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])], [rtc_ok=no]) + AC_MSG_RESULT($rtc_ok) + + dnl --------------------------------------------------------------------- +*/ + +/***************************************************************************/ + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#define INLINE_ELAPSED(INL) static INL double elapsed(ticks t1, ticks t0) \ +{ \ + return (double)t1 - (double)t0; \ +} + +/*----------------------------------------------------------------*/ +/* Solaris */ +#if defined(HAVE_GETHRTIME) && defined(HAVE_HRTIME_T) && !defined(HAVE_TICK_COUNTER) +typedef hrtime_t ticks; + +#define getticks gethrtime + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* AIX v. 4+ routines to read the real-time clock or time-base register */ +#if defined(HAVE_READ_REAL_TIME) && defined(HAVE_TIME_BASE_TO_TIME) && !defined(HAVE_TICK_COUNTER) +typedef timebasestruct_t ticks; + +static __inline ticks getticks(void) +{ + ticks t; + read_real_time(&t, TIMEBASE_SZ); + return t; +} + +static __inline double elapsed(ticks t1, ticks t0) /* time in nanoseconds */ +{ + time_base_to_time(&t1, TIMEBASE_SZ); + time_base_to_time(&t0, TIMEBASE_SZ); + return (((double)t1.tb_high - (double)t0.tb_high) * 1.0e9 + + ((double)t1.tb_low - (double)t0.tb_low)); +} + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PowerPC ``cycle'' counter using the time base register. + */ +#if ((((defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))) || (defined(__MWERKS__) && defined(macintosh)))) || (defined(__IBM_GCC_ASM) && (defined(__powerpc__) || defined(__ppc__)))) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + unsigned int tbl, tbu0, tbu1; + + do { + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0)); + __asm__ __volatile__ ("mftb %0" : "=r"(tbl)); + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1)); + } while (tbu0 != tbu1); + + return (((unsigned long long)tbu0) << 32) | tbl; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* MacOS/Mach (Darwin) time-base register interface (unlike UpTime, + from Carbon, requires no additional libraries to be linked). */ +#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && !defined(HAVE_TICK_COUNTER) +#include +typedef uint64_t ticks; +#define getticks mach_absolute_time +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * Pentium cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC)) && defined(__i386__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + ticks ret; + + __asm__ __volatile__("rdtsc": "=A" (ret)); + /* no input, nothing else clobbered */ + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/* Visual C++ -- thanks to Morten Nissov for his help with this */ +#if _MSC_VER >= 1200 && _M_IX86 >= 500 && !defined(HAVE_TICK_COUNTER) +#include +typedef LARGE_INTEGER ticks; +#define RDTSC __asm __emit 0fh __asm __emit 031h /* hack for VC++ 5.0 */ + +static __inline ticks getticks(void) +{ + ticks retval; + + __asm { + RDTSC + mov retval.HighPart, edx + mov retval.LowPart, eax + } + return retval; +} + +static __inline double elapsed(ticks t1, ticks t0) +{ + return (double)t1.QuadPart - (double)t0.QuadPart; +} + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/*----------------------------------------------------------------*/ +/* + * X86-64 cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC) || defined(__SUNPRO_C)) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + unsigned a, d; + asm volatile("rdtsc" : "=a" (a), "=d" (d)); + return ((ticks)a) | (((ticks)d) << 32); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* PGI compiler, courtesy Cristiano Calonaci, Andrea Tarsi, & Roberto Gori. + NOTE: this code will fail to link unless you use the -Masmkeyword compiler + option (grrr). */ +#if defined(__PGI) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; +static ticks getticks(void) +{ + asm(" rdtsc; shl $0x20,%rdx; mov %eax,%eax; or %rdx,%rax; "); +} +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#endif + +/* Visual C++, courtesy of Dirk Michaelis */ +#if _MSC_VER >= 1400 && (defined(_M_AMD64) || defined(_M_X64)) && !defined(HAVE_TICK_COUNTER) + +#include +#pragma intrinsic(__rdtsc) +typedef unsigned __int64 ticks; +#define getticks __rdtsc +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * IA64 cycle counter + */ + +/* intel's icc/ecc compiler */ +#if (defined(__EDG_VERSION) || defined(__ECC)) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; +#include + +static __inline__ ticks getticks(void) +{ + return __getReg(_IA64_REG_AR_ITC); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* gcc */ +#if defined(__GNUC__) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +static __inline__ ticks getticks(void) +{ + ticks ret; + + __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* HP/UX IA64 compiler, courtesy Teresa L. Johnson: */ +#if defined(__hpux) && defined(__ia64) && !defined(HAVE_TICK_COUNTER) +#include +typedef unsigned long ticks; + +static inline ticks getticks(void) +{ + ticks ret; + + ret = _Asm_mov_from_ar (_AREG_ITC); + return ret; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/* Microsoft Visual C++ */ +#if defined(_MSC_VER) && defined(_M_IA64) && !defined(HAVE_TICK_COUNTER) +typedef unsigned __int64 ticks; + +# ifdef __cplusplus +extern "C" +# endif +ticks __getReg(int whichReg); +#pragma intrinsic(__getReg) + +static __inline ticks getticks(void) +{ + volatile ticks temp; + temp = __getReg(3116); + return temp; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PA-RISC cycle counter + */ +#if defined(__hppa__) || defined(__hppa) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +# ifdef __GNUC__ +static __inline__ ticks getticks(void) +{ + ticks ret; + + __asm__ __volatile__("mfctl 16, %0": "=r" (ret)); + /* no input, nothing else clobbered */ + return ret; +} +# else +# include +static inline unsigned long getticks(void) +{ + register ticks ret; + _MFCTL(16, ret); + return ret; +} +# endif + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* S390, courtesy of James Treacy */ +#if defined(__GNUC__) && defined(__s390__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + ticks cycles; + __asm__("stck 0(%0)" : : "a" (&(cycles)) : "memory", "cc"); + return cycles; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__alpha__) && !defined(HAVE_TICK_COUNTER) +/* + * The 32-bit cycle counter on alpha overflows pretty quickly, + * unfortunately. A 1GHz machine overflows in 4 seconds. + */ +typedef unsigned int ticks; + +static __inline__ ticks getticks(void) +{ + unsigned long cc; + __asm__ __volatile__ ("rpcc %0" : "=r"(cc)); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__sparc_v9__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +static __inline__ ticks getticks(void) +{ + ticks ret; + __asm__ __volatile__("rd %%tick, %0" : "=r" (ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if (defined(__DECC) || defined(__DECCXX)) && defined(__alpha) && defined(HAVE_C_ASM_H) && !defined(HAVE_TICK_COUNTER) +# include +typedef unsigned int ticks; + +static __inline ticks getticks(void) +{ + unsigned long cc; + cc = asm("rpcc %v0"); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +/* SGI/Irix */ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) && !defined(HAVE_TICK_COUNTER) +typedef struct timespec ticks; + +static inline ticks getticks(void) +{ + struct timespec t; + clock_gettime(CLOCK_SGI_CYCLE, &t); + return t; +} + +static inline double elapsed(ticks t1, ticks t0) +{ + return ((double)t1.tv_sec - (double)t0.tv_sec) * 1.0E9 + + ((double)t1.tv_nsec - (double)t0.tv_nsec); +} +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* Cray UNICOS _rtc() intrinsic function */ +#if defined(HAVE__RTC) && !defined(HAVE_TICK_COUNTER) +#ifdef HAVE_INTRINSICS_H +# include +#endif + +typedef long long ticks; + +#define getticks _rtc + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* MIPS ZBus */ +#if HAVE_MIPS_ZBUS_TIMER +#if defined(__mips__) && !defined(HAVE_TICK_COUNTER) +#include +#include +#include + +typedef uint64_t ticks; + +static inline ticks getticks(void) +{ + static uint64_t* addr = 0; + + if (addr == 0) + { + uint32_t rq_addr = 0x10030000; + int fd; + int pgsize; + + pgsize = getpagesize(); + fd = open ("/dev/mem", O_RDONLY | O_SYNC, 0); + if (fd < 0) { + perror("open"); + return NULL; + } + addr = mmap(0, pgsize, PROT_READ, MAP_SHARED, fd, rq_addr); + close(fd); + if (addr == (uint64_t *)-1) { + perror("mmap"); + return NULL; + } + } + + return *addr; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif +#endif /* HAVE_MIPS_ZBUS_TIMER */ + diff --git a/SD-VBS/common/matlab/fSelfCheck.m b/SD-VBS/common/matlab/fSelfCheck.m new file mode 100644 index 0000000..0288328 --- /dev/null +++ b/SD-VBS/common/matlab/fSelfCheck.m @@ -0,0 +1,27 @@ +function ret = fSelfCheck(in1, path, tol) + +r1 = size(in1, 1); +c1 = size(in1, 2); + +ret = 1; + +file = [path, '/expected.m']; +fd = fopen(file, 'r'); + +[in2, count] = fscanf(fd, '%f'); + +if(count ~= (r1*c1) ) + fprintf(1, 'Dimensions mismatch: Expected %d\t Observed %d\n', count, (r1*c1)); + ret = -1; +else + ret = 1; + for i=1:(r1*c1) + if( (abs(in1(i)) - abs(in2(i)) > tol) || (abs(in2(i)) - abs(in1(i))) > tol) + fprintf(1, 'Checking Error: Index %d\tExpected %f\tObserved %f\n', i, in2(i), in1(i)); + ret = -1; + break; + end + end + +end + diff --git a/SD-VBS/common/matlab/fWriteMatrix.m b/SD-VBS/common/matlab/fWriteMatrix.m new file mode 100644 index 0000000..3995aa6 --- /dev/null +++ b/SD-VBS/common/matlab/fWriteMatrix.m @@ -0,0 +1,18 @@ +function fWriteMatrix(input, inpath) + +file = [inpath '/expected.m']; +disp(file); +fd = fopen(file, 'w'); + +[rows, cols] = size(input); + +for j=1:rows + for i=1:cols + fprintf(fd, '%f\t', input(j,i)); + end + fprintf(fd, '\n'); +end +fclose(fd); +end + + diff --git a/SD-VBS/common/matlab/photonEndTiming.c b/SD-VBS/common/matlab/photonEndTiming.c new file mode 100644 index 0000000..bbf5231 --- /dev/null +++ b/SD-VBS/common/matlab/photonEndTiming.c @@ -0,0 +1,15 @@ +#include"mex.h" +#include +#include +#include +#include + +void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) +{ + uint32_t* cycles; + plhs[0] = mxCreateNumericMatrix(1, 2, mxUINT32_CLASS, mxREAL); + cycles = (uint32_t*)mxGetPr(plhs[0]); + __asm__ __volatile__( "rdtsc": "=a" (cycles[0]), "=d" (cycles[1])); + + return; +} diff --git a/SD-VBS/common/matlab/photonEndTiming.mexa64 b/SD-VBS/common/matlab/photonEndTiming.mexa64 new file mode 100755 index 0000000..e41dd3e Binary files /dev/null and b/SD-VBS/common/matlab/photonEndTiming.mexa64 differ diff --git a/SD-VBS/common/matlab/photonEndTiming.mexglx b/SD-VBS/common/matlab/photonEndTiming.mexglx new file mode 100755 index 0000000..fa9d4b6 Binary files /dev/null and b/SD-VBS/common/matlab/photonEndTiming.mexglx differ diff --git a/SD-VBS/common/matlab/photonPrintTiming.m b/SD-VBS/common/matlab/photonPrintTiming.m new file mode 100644 index 0000000..b7c2abb --- /dev/null +++ b/SD-VBS/common/matlab/photonPrintTiming.m @@ -0,0 +1,11 @@ +%! _photonPrintTiming_NA_i2 + +function photonPrintTiming(elapsed) + if(elapsed(2) == 0) + fprintf(1,'Cycles elapsed\t\t- %u\n',elapsed(1)); + else + fprintf(1,'Cycles elapsed\t\t- %u%\u\n',elapsed(2),elapsed(1)); + end +end + + diff --git a/SD-VBS/common/matlab/photonReportTiming.m b/SD-VBS/common/matlab/photonReportTiming.m new file mode 100644 index 0000000..e991175 --- /dev/null +++ b/SD-VBS/common/matlab/photonReportTiming.m @@ -0,0 +1,7 @@ +%! _photonReportTiming_i2_i2i2 + +function elapsed = photonReportTiming(startCycles, endCycles) + elapsed = zeros(1,2); + elapsed(1) = endCycles(1) - startCycles(1); + elapsed(2) = endCycles(2) - startCycles(2); +end \ No newline at end of file diff --git a/SD-VBS/common/matlab/photonStartTiming.c b/SD-VBS/common/matlab/photonStartTiming.c new file mode 100644 index 0000000..bbf5231 --- /dev/null +++ b/SD-VBS/common/matlab/photonStartTiming.c @@ -0,0 +1,15 @@ +#include"mex.h" +#include +#include +#include +#include + +void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) +{ + uint32_t* cycles; + plhs[0] = mxCreateNumericMatrix(1, 2, mxUINT32_CLASS, mxREAL); + cycles = (uint32_t*)mxGetPr(plhs[0]); + __asm__ __volatile__( "rdtsc": "=a" (cycles[0]), "=d" (cycles[1])); + + return; +} diff --git a/SD-VBS/common/matlab/photonStartTiming.mexa64 b/SD-VBS/common/matlab/photonStartTiming.mexa64 new file mode 100755 index 0000000..58e0d06 Binary files /dev/null and b/SD-VBS/common/matlab/photonStartTiming.mexa64 differ diff --git a/SD-VBS/common/matlab/photonStartTiming.mexglx b/SD-VBS/common/matlab/photonStartTiming.mexglx new file mode 100755 index 0000000..8b55b98 Binary files /dev/null and b/SD-VBS/common/matlab/photonStartTiming.mexglx differ diff --git a/SD-VBS/common/matlab/product.m b/SD-VBS/common/matlab/product.m new file mode 100755 index 0000000..65548b4 --- /dev/null +++ b/SD-VBS/common/matlab/product.m @@ -0,0 +1,30 @@ +function [matrixOut] = product(matrixIn, direction) + +% Initialize matrices +inputDim = size(matrixIn); + +% For input matrix (m,n), we need to multiply horizontally if output matrix +%is (m). Else, we multiply vertically. + +%if direction is 1, we multiply vertically. +if direction == 1 + matrixOut = zeros(1, inputDim(2)); %initialize the output matrix + for cols = 1:inputDim(2) + val = 1; + for rows = 1:inputDim(1) + val = matrixIn(rows, cols) * val; + end + matrixOut(cols) = val; + end + +% else multiply horizontally +else + matrixOut = zeros(inputDim(1), 1); %initialize the output matrix + for rows = 1:inputDim(1) + val = 1; + for cols = 1:inputDim(2) + val = matrixIn(rows, cols) * val; + end + matrixOut(rows) = val; + end +end \ No newline at end of file diff --git a/SD-VBS/common/matlab/randWrapper.m b/SD-VBS/common/matlab/randWrapper.m new file mode 100644 index 0000000..dbef132 --- /dev/null +++ b/SD-VBS/common/matlab/randWrapper.m @@ -0,0 +1,17 @@ +function out = randWrapper(m,n) + +out = zeros(m,n); + +seed = 0.9; +for i=1:m + for j=1:n + if(i=1.0) + x1 = 2.0 * rand(1,1) - 1.0; + x2 = 2.0 * rand(1,1) - 1.0; + w = x1*x1 + x2*x2; + end + w = sqrt((-2.0*log(w))/w); + retRand(i,j) = x1*w; + end +end + + + + diff --git a/SD-VBS/common/matlab/randnWrapper.m b/SD-VBS/common/matlab/randnWrapper.m new file mode 100644 index 0000000..1755957 --- /dev/null +++ b/SD-VBS/common/matlab/randnWrapper.m @@ -0,0 +1,17 @@ +function out = randnWrapper(m, n) + +out = ones(m,n); +temp = randWrapper(m,n); + +for i=1:m + for j=1:n + w = temp(i,j); + x1 = 1; + %w = sqrt((-2.0*log(w))/w); + w = ((-2.0*log(w))/w); + out(i,j) = x1*w; + end +end + +end + diff --git a/SD-VBS/common/matlab/readFile.m b/SD-VBS/common/matlab/readFile.m new file mode 100644 index 0000000..4888103 --- /dev/null +++ b/SD-VBS/common/matlab/readFile.m @@ -0,0 +1,22 @@ +function fid = readFile(path) + +file = fopen(path, 'r'); + +full = fscanf(file,'%f'); +elapsed = zeros(1,2); + +rows = full(1); +cols = full(2); +fid = zeros(rows, cols); + +k = 3; +for i=1:rows + for j =1:cols + fid(i,j) = full(k); + k = k+1; + end +end +fclose(file); + +end + diff --git a/SD-VBS/common/matlab/readImage.m b/SD-VBS/common/matlab/readImage.m new file mode 100644 index 0000000..c70222d --- /dev/null +++ b/SD-VBS/common/matlab/readImage.m @@ -0,0 +1,60 @@ +function srcImage = readImage(pathName) + + %Reading BMP image + input = fopen(pathName,'r'); + %start of header information + signature = fread(input, 2, 'uchar'); + file_size = fread(input, 1, 'uint32'); + reserved1 = fread(input, 1, 'uint16'); + reserved2 = fread(input, 1, 'uint16'); + loc_of_bitmap = fread(input, 1, 'uint32'); + + size_of_infoheader = fread(input, 1, 'uint32'); + width = fread(input, 1, 'uint32'); + height = fread(input, 1, 'uint32'); + number_of_planes = fread(input, 1, 'uint16'); + bits_per_pixel = fread(input, 1, 'uint16'); + compression_method = fread(input, 1, 'uint32'); + bytes_of_bitmap = fread(input, 1, 'uint32'); + + hori_reso = fread(input, 1, 'uint32'); + vert_reso = fread(input, 1, 'uint32'); + no_of_colors = fread(input, 1, 'uint32'); + no_of_imp_colors = fread(input, 1, 'uint32'); + + %end of header information + + srcImage = zeros(height, width); + + % Conditions to check whether the BMP is interleaved and handling few exceptions + if (height <= 0 || width <= 0 || signature(1) ~= 'B' || signature(2) ~= 'M' || ( bits_per_pixel ==16)) + disp('Error in file format'); + srcImage = 0; + end + + status = fseek(input,loc_of_bitmap,-1); + + nI = 0; + nJ = 0; + + if(bits_per_pixel == 24) + for nI=height:-1:1 + for nJ=1:width + tempb = fread(input, 1,'uchar'); + tempg = fread(input, 1,'uchar'); + tempr = fread(input, 1,'uchar'); + srcImage(nI,nJ) = uint8((tempb + 6*tempg + 3*tempr)/10); + srcImage(nI,nJ) = uint8(tempg); + end + end + else + for nI=height:-1:1 + for nJ=1:width + tempg = fread(input, 1,'uchar'); + srcImage(nI,nJ) = tempg; + end + end + end + + fclose(input); +end diff --git a/SD-VBS/common/matlab/read_image8u_bmp.m b/SD-VBS/common/matlab/read_image8u_bmp.m new file mode 100755 index 0000000..28e5834 --- /dev/null +++ b/SD-VBS/common/matlab/read_image8u_bmp.m @@ -0,0 +1,66 @@ +function image = read_image8u_bmp(pathName) + + fid = fopen (pathName,'r'); %FILE *input; +% //check for the input FILE pointer + if(fid == 0) + + disp('File pointer error'); + + else +% start of header information + BYTES_PER_PIXEL=3; + signature = fread(fid,2,'char=>char'); + file_size = fread(fid,1,'int=>int'); + reserved1 = fread(fid,1,'short=>short'); + reserved2 = fread(fid,1,'short=>short'); + loc_of_bitmap = fread(fid, 1, 'int=>double'); + size_of_infoheader = fread(fid,1,'int=>int'); + width = fread(fid,1,'int=>double'); + height = fread(fid,1,'int=>double'); + number_of_planes = fread(fid,1,'short=>short'); + bits_per_pixel = fread(fid,1,'short=>short'); + compression_method = fread(fid,1,'int=>int'); + bytes_of_bitmap = fread(fid,1,'int=>int'); + hori_reso = fread(fid,1,'int=>int'); + vert_reso = fread(fid,1,'int=>int'); + no_of_colors = fread(fid,1,'int=>int'); + no_of_imp_colors = fread(fid,1,'int=>int'); + + nRows = height; + nCols = width; + nPitch = nCols; + pad = 4 - (nCols*BYTES_PER_PIXEL - 4*floor(nCols*BYTES_PER_PIXEL/4)); %bitmap multiple-of-4 requirement + + pixSize=nRows *nCols * 3; + + % Conditions to check whether the BMP is interleaved and handling few exceptions + if (nRows <= 0 || nCols <= 0 || signature(1) ~= 'B' || signature(2) ~= 'M' || bits_per_pixel ~=24) + disp ('Error'); + return; + else + +%read image +% fseek(fid,loc_of_bitmap,'bof'); + + for nI = nRows:-1:1 + for nJ=1:nCols + + image(nI,nJ,3) = fread(fid,1,'char=>uint8'); %B + image(nI,nJ,2) = fread(fid,1,'char=>uint8'); %G + image(nI,nJ,1) = fread(fid,1,'char=>uint8'); %R + + end + if pad~=4 + for i=1:pad + fread(fid,1); + end + end + end + imshow(image); + imwrite(image,'image.bmp'); + fclose(fid); + + end + + end + \ No newline at end of file diff --git a/SD-VBS/common/matlab/reshapeMatrix.m b/SD-VBS/common/matlab/reshapeMatrix.m new file mode 100755 index 0000000..f355828 --- /dev/null +++ b/SD-VBS/common/matlab/reshapeMatrix.m @@ -0,0 +1,13 @@ +function [outputMatrix] = reshapeMatrix(inputMatrix, outputSize) + +inputDim = size(inputMatrix); +outputMatrix = zeros(outputSize(1), outputSize(2)); + +k = 1; + +for i = 1:outputSize(2) + for j = 1:outputSize(1) + outputMatrix(j, i) = inputMatrix(k); + k = k+1; + end +end \ No newline at end of file diff --git a/SD-VBS/common/matlab/selfCheck.m b/SD-VBS/common/matlab/selfCheck.m new file mode 100644 index 0000000..55c8de8 --- /dev/null +++ b/SD-VBS/common/matlab/selfCheck.m @@ -0,0 +1,27 @@ +function ret = selfCheck(in1, path, tol) + +r1 = size(in1, 1); +c1 = size(in1, 2); + +ret = 1; + +file = [path, '/expected.m']; +fd = fopen(file, 'r'); + +[in2, count] = fscanf(fd, '%d'); + +if(count ~= (r1*c1) ) + fprintf(1, 'Dimensions mismatch: Expected %d\t Observed %d\n', count, (r1*c1)); + ret = -1; +else + ret = 1; + for i=1:(r1*c1) + if( (abs(in1(i)) - abs(in2(i)) > tol) || (abs(in2(i)) - abs(in1(i))) > tol) + fprintf(1, 'Checking Error: Index %d\tExpected %d\tObserved %d\n', i, in2(i), in1(i)); + ret = -1; + break; + end + end + +end + diff --git a/SD-VBS/common/matlab/testProductFunction.m b/SD-VBS/common/matlab/testProductFunction.m new file mode 100755 index 0000000..682a005 --- /dev/null +++ b/SD-VBS/common/matlab/testProductFunction.m @@ -0,0 +1,21 @@ +function testProductFunction + +matrixSize = zeros(1, 2); +matrixSize(1) = 4; +matrixSize(2) = 5; +inputMatrix = zeros(matrixSize(1), matrixSize(2)); + +for i=1:matrixSize(1) + for j=1:matrixSize(2) + inputMatrix(i,j) = i+j; + end +end + +outputMatrix = product(inputMatrix, 1); +actualOut = prod(inputMatrix, 1); + +if outputMatrix == actualOut + disp('SUCCESS'); +else + disp('ERROR'); +end \ No newline at end of file diff --git a/SD-VBS/common/matlab/testReshapeFunction.m b/SD-VBS/common/matlab/testReshapeFunction.m new file mode 100755 index 0000000..1dac432 --- /dev/null +++ b/SD-VBS/common/matlab/testReshapeFunction.m @@ -0,0 +1,21 @@ +function testReshapeFunction + +matrixSize = zeros(1, 2); +matrixSize(1) = 4; +matrixSize(2) = 5; +inputMatrix = zeros(matrixSize(1), matrixSize(2)); + +for i=1:matrixSize(1) + for j=1:matrixSize(2) + inputMatrix(i,j) = i+j; + end +end + +outputMatrix = reshapeMatrix(inputMatrix, matrixSize); +actualOut = reshape(inputMatrix, matrixSize); + +if outputMatrix == actualOut + disp('SUCCESS'); +else + disp('ERROR'); +end \ No newline at end of file diff --git a/SD-VBS/common/matlab/timingFuncs/cycle.h b/SD-VBS/common/matlab/timingFuncs/cycle.h new file mode 100644 index 0000000..2652a04 --- /dev/null +++ b/SD-VBS/common/matlab/timingFuncs/cycle.h @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2003, 2007-8 Matteo Frigo + * Copyright (c) 2003, 2007-8 Massachusetts Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + + +/* machine-dependent cycle counters code. Needs to be inlined. */ + +/***************************************************************************/ +/* To use the cycle counters in your code, simply #include "cycle.h" (this + file), and then use the functions/macros: + + ticks getticks(void); + + ticks is an opaque typedef defined below, representing the current time. + You extract the elapsed time between two calls to gettick() via: + + double elapsed(ticks t1, ticks t0); + + which returns a double-precision variable in arbitrary units. You + are not expected to convert this into human units like seconds; it + is intended only for *comparisons* of time intervals. + + (In order to use some of the OS-dependent timer routines like + Solaris' gethrtime, you need to paste the autoconf snippet below + into your configure.ac file and #include "config.h" before cycle.h, + or define the relevant macros manually if you are not using autoconf.) +*/ + +/***************************************************************************/ +/* This file uses macros like HAVE_GETHRTIME that are assumed to be + defined according to whether the corresponding function/type/header + is available on your system. The necessary macros are most + conveniently defined if you are using GNU autoconf, via the tests: + + dnl --------------------------------------------------------------------- + + AC_C_INLINE + AC_HEADER_TIME + AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h]) + + AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t is defined in ])],,[#if HAVE_SYS_TIME_H +#include +#endif]) + + AC_CHECK_FUNCS([gethrtime read_real_time time_base_to_time clock_gettime mach_absolute_time]) + + dnl Cray UNICOS _rtc() (real-time clock) intrinsic + AC_MSG_CHECKING([for _rtc intrinsic]) + rtc_ok=yes + AC_TRY_LINK([#ifdef HAVE_INTRINSICS_H +#include +#endif], [_rtc()], [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])], [rtc_ok=no]) + AC_MSG_RESULT($rtc_ok) + + dnl --------------------------------------------------------------------- +*/ + +/***************************************************************************/ + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#define INLINE_ELAPSED(INL) static INL double elapsed(ticks t1, ticks t0) \ +{ \ + return (double)t1 - (double)t0; \ +} + +/*----------------------------------------------------------------*/ +/* Solaris */ +#if defined(HAVE_GETHRTIME) && defined(HAVE_HRTIME_T) && !defined(HAVE_TICK_COUNTER) +typedef hrtime_t ticks; + +#define getticks gethrtime + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* AIX v. 4+ routines to read the real-time clock or time-base register */ +#if defined(HAVE_READ_REAL_TIME) && defined(HAVE_TIME_BASE_TO_TIME) && !defined(HAVE_TICK_COUNTER) +typedef timebasestruct_t ticks; + +static __inline ticks getticks(void) +{ + ticks t; + read_real_time(&t, TIMEBASE_SZ); + return t; +} + +static __inline double elapsed(ticks t1, ticks t0) /* time in nanoseconds */ +{ + time_base_to_time(&t1, TIMEBASE_SZ); + time_base_to_time(&t0, TIMEBASE_SZ); + return (((double)t1.tb_high - (double)t0.tb_high) * 1.0e9 + + ((double)t1.tb_low - (double)t0.tb_low)); +} + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PowerPC ``cycle'' counter using the time base register. + */ +#if ((((defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))) || (defined(__MWERKS__) && defined(macintosh)))) || (defined(__IBM_GCC_ASM) && (defined(__powerpc__) || defined(__ppc__)))) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + unsigned int tbl, tbu0, tbu1; + + do { + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0)); + __asm__ __volatile__ ("mftb %0" : "=r"(tbl)); + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1)); + } while (tbu0 != tbu1); + + return (((unsigned long long)tbu0) << 32) | tbl; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* MacOS/Mach (Darwin) time-base register interface (unlike UpTime, + from Carbon, requires no additional libraries to be linked). */ +#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && !defined(HAVE_TICK_COUNTER) +#include +typedef uint64_t ticks; +#define getticks mach_absolute_time +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * Pentium cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC)) && defined(__i386__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + ticks ret; + + __asm__ __volatile__("rdtsc": "=A" (ret)); + /* no input, nothing else clobbered */ + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/* Visual C++ -- thanks to Morten Nissov for his help with this */ +#if _MSC_VER >= 1200 && _M_IX86 >= 500 && !defined(HAVE_TICK_COUNTER) +#include +typedef LARGE_INTEGER ticks; +#define RDTSC __asm __emit 0fh __asm __emit 031h /* hack for VC++ 5.0 */ + +static __inline ticks getticks(void) +{ + ticks retval; + + __asm { + RDTSC + mov retval.HighPart, edx + mov retval.LowPart, eax + } + return retval; +} + +static __inline double elapsed(ticks t1, ticks t0) +{ + return (double)t1.QuadPart - (double)t0.QuadPart; +} + +#define HAVE_TICK_COUNTER +#define TIME_MIN 5000.0 /* unreliable pentium IV cycle counter */ +#endif + +/*----------------------------------------------------------------*/ +/* + * X86-64 cycle counter + */ +#if (defined(__GNUC__) || defined(__ICC) || defined(__SUNPRO_C)) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + unsigned a, d; + asm volatile("rdtsc" : "=a" (a), "=d" (d)); + return ((ticks)a) | (((ticks)d) << 32); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* PGI compiler, courtesy Cristiano Calonaci, Andrea Tarsi, & Roberto Gori. + NOTE: this code will fail to link unless you use the -Masmkeyword compiler + option (grrr). */ +#if defined(__PGI) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; +static ticks getticks(void) +{ + asm(" rdtsc; shl $0x20,%rdx; mov %eax,%eax; or %rdx,%rax; "); +} +INLINE_ELAPSED(__inline__) +#define HAVE_TICK_COUNTER +#endif + +/* Visual C++, courtesy of Dirk Michaelis */ +#if _MSC_VER >= 1400 && (defined(_M_AMD64) || defined(_M_X64)) && !defined(HAVE_TICK_COUNTER) + +#include +#pragma intrinsic(__rdtsc) +typedef unsigned __int64 ticks; +#define getticks __rdtsc +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * IA64 cycle counter + */ + +/* intel's icc/ecc compiler */ +#if (defined(__EDG_VERSION) || defined(__ECC)) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; +#include + +static __inline__ ticks getticks(void) +{ + return __getReg(_IA64_REG_AR_ITC); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* gcc */ +#if defined(__GNUC__) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +static __inline__ ticks getticks(void) +{ + ticks ret; + + __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/* HP/UX IA64 compiler, courtesy Teresa L. Johnson: */ +#if defined(__hpux) && defined(__ia64) && !defined(HAVE_TICK_COUNTER) +#include +typedef unsigned long ticks; + +static inline ticks getticks(void) +{ + ticks ret; + + ret = _Asm_mov_from_ar (_AREG_ITC); + return ret; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/* Microsoft Visual C++ */ +#if defined(_MSC_VER) && defined(_M_IA64) && !defined(HAVE_TICK_COUNTER) +typedef unsigned __int64 ticks; + +# ifdef __cplusplus +extern "C" +# endif +ticks __getReg(int whichReg); +#pragma intrinsic(__getReg) + +static __inline ticks getticks(void) +{ + volatile ticks temp; + temp = __getReg(3116); + return temp; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* + * PA-RISC cycle counter + */ +#if defined(__hppa__) || defined(__hppa) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +# ifdef __GNUC__ +static __inline__ ticks getticks(void) +{ + ticks ret; + + __asm__ __volatile__("mfctl 16, %0": "=r" (ret)); + /* no input, nothing else clobbered */ + return ret; +} +# else +# include +static inline unsigned long getticks(void) +{ + register ticks ret; + _MFCTL(16, ret); + return ret; +} +# endif + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* S390, courtesy of James Treacy */ +#if defined(__GNUC__) && defined(__s390__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long long ticks; + +static __inline__ ticks getticks(void) +{ + ticks cycles; + __asm__("stck 0(%0)" : : "a" (&(cycles)) : "memory", "cc"); + return cycles; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__alpha__) && !defined(HAVE_TICK_COUNTER) +/* + * The 32-bit cycle counter on alpha overflows pretty quickly, + * unfortunately. A 1GHz machine overflows in 4 seconds. + */ +typedef unsigned int ticks; + +static __inline__ ticks getticks(void) +{ + unsigned long cc; + __asm__ __volatile__ ("rpcc %0" : "=r"(cc)); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__sparc_v9__) && !defined(HAVE_TICK_COUNTER) +typedef unsigned long ticks; + +static __inline__ ticks getticks(void) +{ + ticks ret; + __asm__ __volatile__("rd %%tick, %0" : "=r" (ret)); + return ret; +} + +INLINE_ELAPSED(__inline__) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +#if (defined(__DECC) || defined(__DECCXX)) && defined(__alpha) && defined(HAVE_C_ASM_H) && !defined(HAVE_TICK_COUNTER) +# include +typedef unsigned int ticks; + +static __inline ticks getticks(void) +{ + unsigned long cc; + cc = asm("rpcc %v0"); + return (cc & 0xFFFFFFFF); +} + +INLINE_ELAPSED(__inline) + +#define HAVE_TICK_COUNTER +#endif +/*----------------------------------------------------------------*/ +/* SGI/Irix */ +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) && !defined(HAVE_TICK_COUNTER) +typedef struct timespec ticks; + +static inline ticks getticks(void) +{ + struct timespec t; + clock_gettime(CLOCK_SGI_CYCLE, &t); + return t; +} + +static inline double elapsed(ticks t1, ticks t0) +{ + return ((double)t1.tv_sec - (double)t0.tv_sec) * 1.0E9 + + ((double)t1.tv_nsec - (double)t0.tv_nsec); +} +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* Cray UNICOS _rtc() intrinsic function */ +#if defined(HAVE__RTC) && !defined(HAVE_TICK_COUNTER) +#ifdef HAVE_INTRINSICS_H +# include +#endif + +typedef long long ticks; + +#define getticks _rtc + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif + +/*----------------------------------------------------------------*/ +/* MIPS ZBus */ +#if HAVE_MIPS_ZBUS_TIMER +#if defined(__mips__) && !defined(HAVE_TICK_COUNTER) +#include +#include +#include + +typedef uint64_t ticks; + +static inline ticks getticks(void) +{ + static uint64_t* addr = 0; + + if (addr == 0) + { + uint32_t rq_addr = 0x10030000; + int fd; + int pgsize; + + pgsize = getpagesize(); + fd = open ("/dev/mem", O_RDONLY | O_SYNC, 0); + if (fd < 0) { + perror("open"); + return NULL; + } + addr = mmap(0, pgsize, PROT_READ, MAP_SHARED, fd, rq_addr); + close(fd); + if (addr == (uint64_t *)-1) { + perror("mmap"); + return NULL; + } + } + + return *addr; +} + +INLINE_ELAPSED(inline) + +#define HAVE_TICK_COUNTER +#endif +#endif /* HAVE_MIPS_ZBUS_TIMER */ + diff --git a/SD-VBS/common/matlab/timingFuncs/photonEndTiming.c b/SD-VBS/common/matlab/timingFuncs/photonEndTiming.c new file mode 100644 index 0000000..bbf5231 --- /dev/null +++ b/SD-VBS/common/matlab/timingFuncs/photonEndTiming.c @@ -0,0 +1,15 @@ +#include"mex.h" +#include +#include +#include +#include + +void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) +{ + uint32_t* cycles; + plhs[0] = mxCreateNumericMatrix(1, 2, mxUINT32_CLASS, mxREAL); + cycles = (uint32_t*)mxGetPr(plhs[0]); + __asm__ __volatile__( "rdtsc": "=a" (cycles[0]), "=d" (cycles[1])); + + return; +} diff --git a/SD-VBS/common/matlab/timingFuncs/photonEndTiming.mexglx b/SD-VBS/common/matlab/timingFuncs/photonEndTiming.mexglx new file mode 100755 index 0000000..fa9d4b6 Binary files /dev/null and b/SD-VBS/common/matlab/timingFuncs/photonEndTiming.mexglx differ diff --git a/SD-VBS/common/matlab/timingFuncs/photonPrintTiming.m b/SD-VBS/common/matlab/timingFuncs/photonPrintTiming.m new file mode 100644 index 0000000..b9c7b22 --- /dev/null +++ b/SD-VBS/common/matlab/timingFuncs/photonPrintTiming.m @@ -0,0 +1,5 @@ +%! _photonPrintTiming_NA_i2 + +function photonPrintTiming(elapsed) + disp(elapsed); +end \ No newline at end of file diff --git a/SD-VBS/common/matlab/timingFuncs/photonReportTiming.m b/SD-VBS/common/matlab/timingFuncs/photonReportTiming.m new file mode 100644 index 0000000..e991175 --- /dev/null +++ b/SD-VBS/common/matlab/timingFuncs/photonReportTiming.m @@ -0,0 +1,7 @@ +%! _photonReportTiming_i2_i2i2 + +function elapsed = photonReportTiming(startCycles, endCycles) + elapsed = zeros(1,2); + elapsed(1) = endCycles(1) - startCycles(1); + elapsed(2) = endCycles(2) - startCycles(2); +end \ No newline at end of file diff --git a/SD-VBS/common/matlab/timingFuncs/photonStartTiming.c b/SD-VBS/common/matlab/timingFuncs/photonStartTiming.c new file mode 100644 index 0000000..bbf5231 --- /dev/null +++ b/SD-VBS/common/matlab/timingFuncs/photonStartTiming.c @@ -0,0 +1,15 @@ +#include"mex.h" +#include +#include +#include +#include + +void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) +{ + uint32_t* cycles; + plhs[0] = mxCreateNumericMatrix(1, 2, mxUINT32_CLASS, mxREAL); + cycles = (uint32_t*)mxGetPr(plhs[0]); + __asm__ __volatile__( "rdtsc": "=a" (cycles[0]), "=d" (cycles[1])); + + return; +} diff --git a/SD-VBS/common/matlab/timingFuncs/photonStartTiming.mexglx b/SD-VBS/common/matlab/timingFuncs/photonStartTiming.mexglx new file mode 100755 index 0000000..8b55b98 Binary files /dev/null and b/SD-VBS/common/matlab/timingFuncs/photonStartTiming.mexglx differ diff --git a/SD-VBS/common/matlab/writeMatrix.m b/SD-VBS/common/matlab/writeMatrix.m new file mode 100644 index 0000000..ec2bbae --- /dev/null +++ b/SD-VBS/common/matlab/writeMatrix.m @@ -0,0 +1,19 @@ +function writeMatrix(input, inpath) + +file = [inpath '/expected.m']; +disp(file); +fd = fopen(file, 'w'); + +[rows, cols] = size(input); + +for j=1:rows + for i=1:cols + fprintf(fd, '%d\t', input(j,i)-1); + end + fprintf(fd, '\n'); +end +fclose(fd); + +end + + diff --git a/SD-VBS/common/matlab/write_image8u_bmp.m b/SD-VBS/common/matlab/write_image8u_bmp.m new file mode 100755 index 0000000..71b48ef --- /dev/null +++ b/SD-VBS/common/matlab/write_image8u_bmp.m @@ -0,0 +1,88 @@ +function image = write_image8u_bmp(inpImage, pathName) % this is matlab's sequence, removed width, height since they can be inferred from image + +BYTES_PER_PIXEL = 3; + +fid = fopen (pathName,'w'); %FILE *input; +% //check for the input FILE pointer +if(fid == 0) + disp('File pointer error'); +else + height = size(inpImage,1); + width=size(inpImage,2); + signature(1) = 'B'; + signature(2) = 'M'; + + reserved1 = 0; + reserved2 = 0; + file_size = (height*width*BYTES_PER_PIXEL)+54; + offset=54; + size_of_infoheader=40; + number_of_planes=1; + bits_per_pixel= 8*BYTES_PER_PIXEL ; + compression_method=0; + hori_reso=2835; + vert_reso=2835; + no_of_colors=0; + no_of_imp_colors=0; + +% start of header information + fwrite(fid,signature(1),'char'); + fwrite(fid,signature(2),'char'); + fwrite(fid,file_size,'int'); + fwrite(fid,reserved1,'short'); + fwrite(fid,reserved2,'short'); + fwrite(fid,offset,'int'); + fwrite(fid,size_of_infoheader,'int'); + fwrite(fid,width,'int'); + fwrite(fid,height,'int'); + fwrite(fid,number_of_planes,'short'); + fwrite(fid,bits_per_pixel,'short'); + fwrite(fid,compression_method,'int'); + + bytes_of_bitmap=width*height*BYTES_PER_PIXEL; + + fwrite(fid,bytes_of_bitmap,'int'); + fwrite(fid,hori_reso,'int'); + fwrite(fid,vert_reso,'int'); + fwrite(fid,no_of_colors,'int'); + fwrite(fid,no_of_imp_colors,'int'); + + % Conditions to check whether the BMP is gray scaled and handling few exceptions + + if (width <= 0 || height <= 0 || signature(1) ~= 'B' || signature(2) ~= 'M') + return; + end + + % total size of pixels + % pixSize=srcImage->height * srcImage->width * NO_OF_BYTE_PER_PIXEL; //*3 is done as it is a 3 channel image + %reverse the image back + + for nI=1:height + count = 0; + for nJ=1:width + temp = inpImage((height - nI+1),nJ,3); + fwrite(fid,temp,'char'); + temp = inpImage((height - nI+1),nJ,2); + fwrite(fid,temp,'char'); + temp = inpImage((height - nI+1),nJ,1); + fwrite(fid,temp,'char'); + + end + pad = 4 - (width*BYTES_PER_PIXEL - (4*floor(width*BYTES_PER_PIXEL/4))); %bitmap multiple-of-4 requirement + if pad ~= 0 + temp=0; + for cnt = 1:pad + fwrite(fid,temp,'char'); +% fwrite(fid,temp,'char'); +% fwrite(fid,temp,'char'); + end + end + end + + outImage = imread(pathName); + imshow(outImage); + fclose(fid); + +end + + \ No newline at end of file diff --git a/SD-VBS/common/support/buildTable.py b/SD-VBS/common/support/buildTable.py new file mode 100644 index 0000000..1a1a6aa --- /dev/null +++ b/SD-VBS/common/support/buildTable.py @@ -0,0 +1,54 @@ +import re +import sys +import os + +def getExprName(fileName): + exprFileName = os.path.basename(fileName) + exprName = re.sub("\.txt", "", exprFileName) + exprName = re.sub("_","\t", exprName, 1) + exprName = re.sub("_", "", exprName) + return(exprName) + +def getBmrkName(fileName): + benchmarkDir = os.path.dirname(fileName) + benchmarkName = os.path.basename(benchmarkDir) + return(benchmarkName) + +def getCycleCountTup(fileName): + try: + inp = open(fileName, 'r') + except IOError, err: + print "ERROR: Could not open the inputFile:"+fileName + sys.exit(1) + globalTup = None + for currline in inp: + currline = currline.strip() + currline = currline.lower() + if(re.match("cycles elapsed", currline)): + valTup = re.findall("(\d+)", currline) + return(valTup) + + if(globalTup != None): + return(globalTup) + else: + print "ERROR: INVALID INPUT FILE:"+fileName + sys.exit(2) + +def main(): + if(len(sys.argv)<2): + print "USAGE: "+sys.argv[0]+" " + sys.exit(1) + + fileName = sys.argv[1] + exprName = getExprName(fileName) + benchmarkName = getBmrkName(fileName) + cycleCountTup = getCycleCountTup(fileName) + + print "%-20s %-20s %10s\n" % (benchmarkName,exprName,cycleCountTup[0]) + + + + + +if __name__=="__main__": + main() diff --git a/SD-VBS/common/support/buildTimeTable.py b/SD-VBS/common/support/buildTimeTable.py new file mode 100644 index 0000000..c718cb2 --- /dev/null +++ b/SD-VBS/common/support/buildTimeTable.py @@ -0,0 +1,55 @@ +import re +import sys +import os + +def getExprName(fileName): + exprFileName = os.path.basename(fileName) + exprName = re.sub("\.txt", "", exprFileName) + exprName = re.sub("_","\t", exprName, 1) + exprName = re.sub("_", "", exprName) + return(exprName) + +def getBmrkName(fileName): + benchmarkDir = os.path.dirname(fileName) + benchmarkName = os.path.basename(benchmarkDir) + return(benchmarkName) + +def getTimeTup(fileName): + try: + inp = open(fileName, 'r') + except IOError, err: + print "ERROR: Could not open the inputFile:"+fileName + sys.exit(1) + globalTup = None + for currline in inp: + currline = currline.strip() + currline = currline.lower() + if(re.match("real", currline)): + #valTup = re.findall("(\d+)", currline) + + return(currline[4:].strip()) + + if(globalTup != None): + return(globalTup) + else: + print "ERROR: INVALID INPUT FILE:"+fileName + sys.exit(2) + +def main(): + if(len(sys.argv)<2): + print "USAGE: "+sys.argv[0]+" " + sys.exit(1) + + fileName = sys.argv[1] + exprName = getExprName(fileName) + benchmarkName = getBmrkName(fileName) + timeTup = getTimeTup(fileName) + + print "%-20s %-20s %10s\n" % (benchmarkName,exprName,timeTup) + + + + + +if __name__=="__main__": + main() diff --git a/SD-VBS/common/toolbox/MultiNcut/MNcut.m b/SD-VBS/common/toolbox/MultiNcut/MNcut.m new file mode 100755 index 0000000..5486080 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/MNcut.m @@ -0,0 +1,93 @@ +function [NcutDiscretes,eigenVectors,eigenValues] = MNcut(I,nsegs); +% +% [NcutDiscrete,eigenVectors,eigenValues] = MNcut(I,nsegs); +% +% + +[nr,nc,nb] = size(I); + +max_image_size = max(nr,nc); + +% modified by song, 06/13/2005 +% test parameters +if (1) % original settings + if (max_image_size>120) & (max_image_size<=500), + % use 3 levels, + data.layers.number=3; + data.layers.dist=3; + data.layers.weight=[3000,4000,10000]; + data.W.scales=[1,2,3];%[1,2,3]; + data.W.radius=[2,3,7];%[2,3,7]; + elseif (max_image_size >500), + % use 4 levels, + data.layers.number=4; + data.layers.dist=3; + data.layers.weight=[3000,4000,10000,20000]; + data.W.scales=[1,2,3,3]; + data.W.radius=[2,3,4,6]; + elseif (max_image_size <=120) + data.layers.number=2; + data.layers.dist=3; + data.layers.weight=[3000,10000]; + data.W.scales=[1,2]; + data.W.radius=[2,6]; + end +else % test setting + if (max_image_size>200) & (max_image_size<=500), + % use 3 levels, + data.layers.number=3; + data.layers.dist=3; + data.layers.weight=[3000,4000,10000]; + data.W.scales=[1,2,3];%[1,2,3]; + data.W.radius=[2,3,7];%[2,3,7]; + elseif (max_image_size >500), + % use 4 levels, + data.layers.number=4; + data.layers.dist=3; + data.layers.weight=[3000,4000,10000,20000]; + data.W.scales=[1,2,3,3]; + data.W.radius=[2,3,4,6]; + elseif (max_image_size <=200) + data.layers.number=2; + data.layers.dist=3; + data.layers.weight=[3000,10000]; + data.W.scales=[1,2]; + data.W.radius=[2,4]; + end + +end; + + +data.W.edgeVariance=0.1; %0.1 +data.W.gridtype='square'; +data.W.sigmaI=0.12;%0.12 +data.W.sigmaX=1000; +data.W.mode='mixed'; +data.W.p=0; +data.W.q=0; + +%eigensolver +data.dataGraphCut.offset = 100;% 10; %valeur sur diagonale de W (mieux vaut 10 pour valeurs negatives de W) +data.dataGraphCut.maxiterations=50;% voir +data.dataGraphCut.eigsErrorTolerance=1e-2;%1e-6; +data.dataGraphCut.valeurMin=1e-6;%1e-5;% utilise pour tronquer des valeurs et sparsifier des matrices +data.dataGraphCut.verbose = 0; + +data.dataGraphCut.nbEigenValues=max(nsegs); + +disp('computeEdge'); +[multiWpp,ConstraintMat, Wind,data,emag,ephase]= computeMultiW (I,data); + +disp('Ncut'); +[eigenVectors,eigenValues]= eigSolve (multiWpp,ConstraintMat,data); + +%NcutDiscretes = zeros(nr,nc,length(nsegs)); +NcutDiscretes = zeros(nr,nc,(nsegs)); + +for j=1:length(nsegs), + nseg = nsegs(j); + [nr,nc,nb] = size(eigenVectors(:,:,1:nseg)); + [NcutDiscrete,evrotated] =discretisation(reshape(eigenVectors(:,:,1:nb),nr*nc,nb),nr,nc); + NcutDiscretes(:,:,j) = NcutDiscrete; +end + diff --git a/SD-VBS/common/toolbox/MultiNcut/MNcutDemo.m b/SD-VBS/common/toolbox/MultiNcut/MNcutDemo.m new file mode 100755 index 0000000..972a4eb --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/MNcutDemo.m @@ -0,0 +1,34 @@ +% MNcutDemo.m +% created by song, 06/13/2005 +% an exmaple of how to use and display MNcut + +num_segs = [20]; +imageSize = 800; + +img_filename = '/u/ikkjin/Benchmark/stitch/data/test/capitol/img1.jpg'; + +I=readimage(img_filename,imageSize); + +[SegLabel,eigenVectors,eigenValues]= MNcut(I,num_segs); + +for j=1:size(SegLabel,3), + [gx,gy] = gradient(SegLabel(:,:,j)); + bw = (abs(gx)>0.1) + (abs(gy) > 0.1); + + figure(1);clf; J1=showmask(double(I),bw); imagesc(J1);axis image; axis off; + set(gca, 'Position', [0 0 1 1]); + + % cm = sprintf('print -djpeg %s/file%.4d-%.2d.jpg',OutputDir,id,num_segs(j)); disp(cm);eval(cm); + + + % figure(10);imagesc(SegLabel(:,:,j));axis image; axis off; + % set(gca, 'Position', [0 0 1 1]); + % cm = sprintf('print -djpeg %s/Seg%.4d-%.2d.jpg',OutputDir,id,num_segs(j));disp(cm);eval(cm); + + % pause; +end + +% fname = files(id).name; +%cm = sprintf('save %s/SegLabl%.4d.mat I SegLabel fname',OutputDir,id); disp(cm); eval(cm); +%cm = sprintf('save %s/SegEig%.4d.mat eigenVectors eigenValues',OutputDir,id);disp(cm); eval(cm); + diff --git a/SD-VBS/common/toolbox/MultiNcut/README.tex b/SD-VBS/common/toolbox/MultiNcut/README.tex new file mode 100755 index 0000000..5970fb2 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/README.tex @@ -0,0 +1,9 @@ +1) You need to first compile the .c files,type + +>> compileAll('.'); + +2) the top level function is called MNcut.m + + + + diff --git a/SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.c b/SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.c new file mode 100755 index 0000000..25def92 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/a_times_b_cmplx.c @@ -0,0 +1,405 @@ +/*================================================================ +a_times_b_cmplx.c = used by a couple of mex functions +provide Matrix vector multiplications, +and solve triangular systems +(sparse matrix and full vector) + +CSC_CmplxVecMult_CAB_double, CSR_CmplxVecMult_CAB_double, +CSCsymm_CmplxVecMult_CAB_double added by Mirko Visontai (10/24/2003) + +*=================================================================*/ +# include "math.h" + + +/*C<-a*A*B+C*/ +void CSC_VecMult_CaABC_double( + const int m, const int k, const double alpha, + const double *val, const int *indx, + const int *pntrb, + const double *b, + double *c) +{ + int i,j,jb,je; + + for (i=0;i!=k;i++){ + jb = pntrb[i]; + je = pntrb[i+1]; + for (j=jb;j!=je;j++) + c[indx[j]] += alpha * b[i] * val[j]; + } +} + +/*C<-a*A'*B+C*/ +void CSR_VecMult_CaABC_double( + const int k, const int m, const double alpha, + const double *val, const int *indx, + const int *pntrb, + const double *b, + double *c) +{ + double t; + const double *pval; + int i,j,jb,je; + + pval = val; + for (i=0;i!=m;i++) { + t = 0; + jb = pntrb[i]; + je = pntrb[i+1]; + for (j=jb;j!=je;j++) + t += alpha * b[indx[j]] * (*pval++); + c[i] += t; + } +} + + +/*C<-A*b */ +void CSC_VecMult_CAB_double( + const int m, const int k, /*nb_rows, nb_columns*/ + const double *val, const int *indx, + const int *pntrb, + const double *b, + double *c + ) +{ + int i,j,jb,je; + double *pc=c; + for (i=0;i!=m;i++) *pc++ = 0; + + for (i=0;i!=k;i++){ + jb = pntrb[i]; + je = pntrb[i+1]; + for (j=jb;j!=je;j++) + c[indx[j]] += b[i] * val[j]; + } +} + +/*C<-A*b (complex)*/ +void CSC_CmplxVecMult_CAB_double( + const int m, const int k, + const double *valr, const double *vali, + const int *indx, + const int *pntrb, + const double *br, const double *bi, + double *cr, double *ci + ) +{ + int i,j,jb,je; + double *pcr=cr; + double *pci=ci; + for (i=0;i!=m;i++){ + *pcr++ = 0.0; + *pci++ = 0.0; + } + + for (i=0;i!=k;i++){ + jb = pntrb[i]; + je = pntrb[i+1]; + for (j=jb;j!=je;j++){ + cr[indx[j]] += (br[i] * valr[j]) - (bi[i] * vali[j]); + ci[indx[j]] += (br[i] * vali[j]) + (bi[i] * valr[j]); + } + } +} + +/*C<-A'*b + plus rapide que CSC_VecMult_CAB_double */ +void CSR_VecMult_CAB_double( + const int k, const int m, + const double *val, const int *indx, + const int *pntrb, + const double *b, + double *c + ) +{ + double t; + const double *pval; + double *pc=c; + int i,j,jb,je; + + for (i=0;i!=m;i++) *pc++ = 0; + + pval = val; + for (i=0;i!=m;i++) { + t = 0; + jb = pntrb[i]; + je = pntrb[i+1]; + for (j=jb;j!=je;j++) + t += b[indx[j]] * (*pval++); + c[i] += t; + } +} + +/*C<-A'*b (complex) + plus rapide que CSC_VecMult_CAB_double */ +void CSR_CmplxVecMult_CAB_double( + const int k, const int m, + const double *valr, const double *vali, + const int *indx, + const int *pntrb, + const double *br, const double *bi, + double *cr, double *ci + ) +{ + double tr, ti; + const double *pvalr; + const double *pvali; + double *pcr=cr; + double *pci=ci; + int i,j,jb,je; + + for (i=0;i!=m;i++){ + *pcr++ = 0.0; + *pci++ = 0.0; + } + + pvalr = valr; + pvali = vali; + for (i=0;i!=m;i++) { + tr = 0.0; + ti = 0.0; + jb = pntrb[i]; + je = pntrb[i+1]; + for (j=jb;j!=je;j++){ + tr += (br[indx[j]] * (*pvalr)) - (bi[indx[j]] * (*pvali)); + ti += (br[indx[j]] * (*pvali++)) + (bi[indx[j]] * (*pvalr++)); + } + cr[i] += tr; + ci[i] += ti; + } +} + + + +/* C<-A*b (A is symmetric) */ +void CSRsymm_VecMult_CAB_double( + const int k, const int m, + const double *val, const int *indx, + const int *pntrb, + const double *b, + double *c + ) +{ + const double *pval; + double *pc=c; + int i,j; + int jj; + int rpntrb, rpntre; + int index, nvals; + + + for (i=0;i!=m;i++) *pc++ = 0; + pval = val; + for (j=0;j!=k;j++){ + rpntrb = pntrb[j]; + rpntre = pntrb[j+1]; + for (jj=rpntrb;jj!=rpntre;jj++) { + index = indx[jj]; + if ( index == j ) { + c[j] += b[j] * (*pval++); + continue; + } + if ( index > j ) { + c[index] += b[j] * (*pval); + + c[j] += b[index] * (*pval++); + } + else { + pval++; + } + } + } +} + + +/* C<-A*b (A is symmetric and complex) */ +void CSRsymm_CmplxVecMult_CAB_double( + const int k, const int m, + const double *valr, const double *vali, + const int *indx, + const int *pntrb, + const double *br, const double *bi, + double *cr, double *ci + ) +{ + const double *pvalr, *pvali; + double *pcr=cr; + double *pci=ci; + int i,j; + int jj; + int rpntrb, rpntre; + int index, nvals; + + + for (i=0;i!=m;i++){ + *pcr++ = 0.0; + *pci++ = 0.0; + } + + pvalr = valr; + pvali = vali; + for (j=0;j!=k;j++){ + rpntrb = pntrb[j]; + rpntre = pntrb[j+1]; + for (jj=rpntrb;jj!=rpntre;jj++) { + index = indx[jj]; + if ( index == j ) { + cr[j] += (br[j] * (*pvalr)) - (bi[j] * (*pvali)); + ci[j] += (br[j] * (*pvali++)) + (bi[j] * (*pvalr++)); + continue; + } + if ( index > j ) { + cr[index] += (br[j] * (*pvalr)) - (bi[j] * (*pvali)); + ci[index] += (br[j] * (*pvali)) + (bi[j] * (*pvalr)); + + cr[j] += (br[index] * (*pvalr)) - (bi[index] * (*pvali)); + ci[j] += (br[index] * (*pvali++)) + (bi[index] * (*pvalr++)); + } + else { + pvalr++; + pvali++; + } + + } + } +} + + +/*C<-A\B; with Lower triangular A*/ +void CSC_VecTriangSlvLD_CAB_double( + const int m, + const double *val, + const int *indx, const int *pntrb, + const double *b, + double *c) +{ + int i, j, jb, je; + double *pc=c; + double z; + + for (i=0;i!=m;i++){ + *pc = b[i]; + pc++; + } + + pc=c; + for (i=0;i!=m;i++) { + jb = pntrb[i]; + je = pntrb[i+1]; + z = pc[i] / val[jb]; + pc[i] = z; + for (j=jb+1;j1) { + mexErrMsgTxt("Too many output arguments"); + } + + /* get edgel information */ + nr = mxGetM(in[0]); + nc = mxGetN(in[0]); + if ( nr*nc ==0 || nr != mxGetM(in[1]) || nc != mxGetN(in[1]) ) { + mexErrMsgTxt("Edge magnitude and phase shall be of the same image size"); + } + emag = mxGetPr(in[0]); + ephase = mxGetPr(in[1]); + np = nr * nc; + + /* get new index pair */ + if (!mxIsUint32(in[2]) | !mxIsUint32(in[3])) { + mexErrMsgTxt("Index pair shall be of type UINT32"); + } + if (mxGetM(in[3]) * mxGetN(in[3]) != np + 1) { + mexErrMsgTxt("Wrong index representation"); + } + pi = mxGetData(in[2]); + pj = mxGetData(in[3]); + + /* create output */ + out[0] = mxCreateSparse(np,np,pj[np],mxREAL); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough memory for the output matrix"); + } + w = mxGetPr(out[0]); + ir = mxGetIr(out[0]); + jc = mxGetJc(out[0]); + + /* find my sigma */ + if (nargin<5) { + sigma = 0; + for (k=0; ksigma) { sigma = emag[k]; } + } + sigma = sigma / 6; + /* printf("sigma = %6.5f",sigma); */ + } else { + sigma = mxGetScalar(in[4]); + } + a = 0.5 / (sigma * sigma); + + /* computation */ + total = 0; + for (j=0; j= abs(dj)) { + slope = dj / di; + step = (iy>=jy) ? 1 : -1; + + iip1 = jy; + jjp1 = jx; + + + for (ii=0;ii maxori){ + maxori = z; + } + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + + /* sample in j direction */ + } else { + slope = di / dj; + step = (ix>=jx) ? 1: -1; + + jjp1 = jx; + iip1 = jy; + + + for (jj=0;jj maxori){ + maxori = z; + } + + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + } + + maxori = 0.5 * maxori; + maxori = exp(-maxori * maxori * a); + } + ir[total] = i; + + w[total] = maxori; + total = total + 1; + + } /* i */ + } /* j */ + + jc[np] = total; +} diff --git a/SD-VBS/common/toolbox/MultiNcut/affinityic.dll b/SD-VBS/common/toolbox/MultiNcut/affinityic.dll new file mode 100755 index 0000000..67e4d64 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/affinityic.dll differ diff --git a/SD-VBS/common/toolbox/MultiNcut/affinityic.mexa64 b/SD-VBS/common/toolbox/MultiNcut/affinityic.mexa64 new file mode 100755 index 0000000..2648387 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/affinityic.mexa64 differ diff --git a/SD-VBS/common/toolbox/MultiNcut/affinityic.mexglx b/SD-VBS/common/toolbox/MultiNcut/affinityic.mexglx new file mode 100755 index 0000000..c296845 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/affinityic.mexglx differ diff --git a/SD-VBS/common/toolbox/MultiNcut/batch_MNcut.m b/SD-VBS/common/toolbox/MultiNcut/batch_MNcut.m new file mode 100755 index 0000000..d4dcb3c --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/batch_MNcut.m @@ -0,0 +1,48 @@ +data_dir = '/data/insecure/qihuizhu/baseball/Gray/train/'; +save_dir = '/home/songgang/project/MultiNcut/batch_result_MNcut'; + +num_segs = [20]; +imageSize = 200; + + +filelist = dir(fullfile(data_dir, '*.tif')); + +nb_file = max(size(filelist)); + + +tic; +for ii = 1:nb_file + fprintf(2, 'Segmenting image: %s ...\n', filelist(ii).name); + + img_filename = fullfile(data_dir, filelist(ii).name); + I=readimage(img_filename,imageSize); + + + [SegLabel,eigenVectors,eigenValues]= MNcut(I,num_segs); + + for j=1:size(SegLabel,3), + [gx,gy] = gradient(SegLabel(:,:,j)); + bw = (abs(gx)>0.1) + (abs(gy) > 0.1); + + figure(1);clf; J1=showmask(double(I),bw); imagesc(J1);axis image; axis off; + set(gca, 'Position', [0 0 1 1]); + set(gca, 'Position', [0 0 1 1]); + [PATHSTR,NAME,EXT,VERSN] = fileparts(filelist(ii).name); + print('-f1', '-djpeg90', fullfile(save_dir, sprintf('%s%s-%d.jpg', NAME,'-out', num_segs(j)))); + + + % cm = sprintf('print -djpeg %s/file%.4d-%.2d.jpg',OutputDir,id,num_segs(j)); disp(cm);eval(cm); + + + % figure(10);imagesc(SegLabel(:,:,j));axis image; axis off; + % set(gca, 'Position', [0 0 1 1]); + % cm = sprintf('print -djpeg %s/Seg%.4d-%.2d.jpg',OutputDir,id,num_segs(j));disp(cm);eval(cm); + +% keyboard; + end + + + +end; +toc; +fprintf(2, ' %d files done\n', nb_file); diff --git a/SD-VBS/common/toolbox/MultiNcut/cimgnbmap.c b/SD-VBS/common/toolbox/MultiNcut/cimgnbmap.c new file mode 100755 index 0000000..44af715 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/cimgnbmap.c @@ -0,0 +1,198 @@ +/*================================================================ +* function [i,j] = cimgnbmap([nr,nc], nb_r, sample_rate) +* computes the neighbourhood index matrix of an image, +* with each neighbourhood sampled. +* Input: +* [nr,nc] = image size +* nb_r = neighbourhood radius, could be [r_i,r_j] for i,j +* sample_rate = sampling rate, default = 1 +* Output: +* [i,j] = each is a column vector, give indices of neighbour pairs +* UINT32 type +* i is of total length of valid elements, 0 for first row +* j is of length nr * nc + 1 +* +* See also: imgnbmap.c, id2cind.m +* +* Examples: +* [i,j] = imgnbmap(10, 20); % [10,10] are assumed +* +* Stella X. Yu, Nov 12, 2001. + +% test sequence: +nr = 15; +nc = 15; +nbr = 1; +[i,j] = cimgnbmap([nr,nc], nbr); +mask = csparse(i,j,ones(length(i),1),nr*nc); +show_dist_w(rand(nr,nc),mask) + +*=================================================================*/ + +# include "mex.h" +# include "math.h" + +void mexFunction( + int nargout, + mxArray *out[], + int nargin, + const mxArray *in[] +) +{ + /* declare variables */ + int nr, nc, np, nb, total; + double *dim, sample_rate; + int r_i, r_j, a1, a2, b1, b2, self, neighbor; + int i, j, k, s, t, nsamp, th_rand, no_sample; + /* unsigned long *p, *qi, *qj; */ + unsigned int *p, *qi, *qj; + + /* check argument */ + if (nargin < 2) { + mexErrMsgTxt("Two input arguments required"); + } + if (nargout> 2) { + mexErrMsgTxt("Too many output arguments."); + } + + /* get image size */ + i = mxGetM(in[0]); + j = mxGetN(in[0]); + dim = mxGetData(in[0]); + nr = (int)dim[0]; + if (j>1 || i>1) { + nc = (int)dim[1]; + } else { + nc = nr; + } + np = nr * nc; + + /* get neighbourhood size */ + i = mxGetM(in[1]); + j = mxGetN(in[1]); + dim = mxGetData(in[1]); + r_i = (int)dim[0]; + if (j>1 || i>1) { + r_j = (int)dim[1]; + } else { + r_j = r_i; + } + if (r_i<0) { r_i = 0; } + if (r_j<0) { r_j = 0; } + + /* get sample rate */ + if (nargin==3) { + sample_rate = (mxGetM(in[2])==0) ? 1: mxGetScalar(in[2]); + } else { + sample_rate = 1; + } + /* prepare for random number generator */ + if (sample_rate<1) { + srand( (unsigned)time( NULL ) ); + th_rand = (int)ceil((double)RAND_MAX * sample_rate); + no_sample = 0; + } else { + sample_rate = 1; + th_rand = RAND_MAX; + no_sample = 1; + } + + /* figure out neighbourhood size */ + + nb = (r_i + r_i + 1) * (r_j + r_j + 1); + if (nb>np) { + nb = np; + } + nb = (int)ceil((double)nb * sample_rate); +/*printf("nb=%d\n",nb);*/ + /* intermediate data structure */ + /* p = mxCalloc(np * (nb+1), sizeof(unsigned long)); */ + p = mxCalloc(np * (nb+1), sizeof(unsigned int)); + if (p==NULL) { + mexErrMsgTxt("Not enough space for my computation."); + } + + /* computation */ + total = 0; + for (j=0; j=nc) { b2 = nc-1; } + + /* i range */ + a1 = i - r_i; + if (a1<0) { a1 = 0; } + a2 = i + r_i; + if (a2>=nr) { a2 = nr-1; } + + /* number of more samples needed */ + nsamp = nb - p[self]; + /*if (nsamp<0) + {printf("nsamp=%d\n",nsamp);}*/ + k = 0; + t = b1; + s = i + 1; + if (s>a2) { + s = a1; + t = t + 1; + } + + + while (ka2) { + s = a1; + t = t + 1; + } + } /* k */ + total = total + p[self]; + } /* i */ + + } /* j */ + + /* i, j */ + + out[0] = mxCreateNumericMatrix(total, 1, mxUINT32_CLASS, mxREAL); + out[1] = mxCreateNumericMatrix(np+1, 1, mxUINT32_CLASS, mxREAL); + qi = mxGetData(out[0]); + qj = mxGetData(out[1]); + + if (out[0]==NULL || out[1]==NULL) { + mexErrMsgTxt("Not enough space for the output matrix."); + } + + total = 0; + for (j=0; j 2) { + mexErrMsgTxt("Too many output arguments."); + } + + /* get image size */ + i = mxGetM(in[0]); + j = mxGetN(in[0]); + dim = mxGetData(in[0]); + nr = (int)dim[0]; + if (j>1 || i>1) { + nc = (int)dim[1]; + } else { + nc = nr; + } + np = nr * nc; + + /* get neighbourhood size */ + i = mxGetM(in[1]); + j = mxGetN(in[1]); + dim = mxGetData(in[1]); + r_i = (int)dim[0]; + if (j>1 || i>1) { + r_j = (int)dim[1]; + } else { + r_j = r_i; + } + if (r_i<0) { r_i = 0; } + if (r_j<0) { r_j = 0; } + + /* get sample rate */ + if (nargin==3) { + sample_rate = (mxGetM(in[2])==0) ? 1: mxGetScalar(in[2]); + } else { + sample_rate = 1; + } + /* prepare for random number generator */ + if (sample_rate<1) { + srand( (unsigned)time( NULL ) ); + th_rand = (int)ceil((double)RAND_MAX * sample_rate); + no_sample = 0; + } else { + sample_rate = 1; + th_rand = RAND_MAX; + no_sample = 1; + } + + /* figure out neighbourhood size */ + + nb = (r_i + r_i) * (r_j + r_j)+1; + if (nb>np) { + nb = np; + } + nb = (int)ceil((double)nb * sample_rate); +/*printf("nb=%d\n",nb);*/ + /* intermediate data structure */ + /* p = mxCalloc(np * (nb+1), sizeof(unsigned long)); */ + p = mxCalloc(np * (nb+1), sizeof(unsigned int)); + if (p==NULL) { + mexErrMsgTxt("Not enough space for my computation."); + } + + /* computation */ + total = 0; + for (j=0; j=nc) { b2 = nc-1; } + + /* i range */ + /*a1 = i - r_i; + if (a1<0) { a1 = 0; }*/ + a2 = i + r_i; + if (a2>=nr) { a2 = nr-1; } + + /* number of more samples needed */ + nsamp = nb - p[self]; + /*if (nsamp<0) + {printf("nsamp=%d\n",nsamp);}*/ + k = 0; + t = b1; + s = i + 1; + if (s>a2) { + s = i; + t = t + 1; + } + + + while (ka2) { s = i; t = t + 1; + } + } + else {t=t+1;} + } /* k */ + total = total + p[self]; + } /* i */ + + } /* j */ + + /* i, j */ + + out[0] = mxCreateNumericMatrix(total, 1, mxUINT32_CLASS, mxREAL); + out[1] = mxCreateNumericMatrix(np+1, 1, mxUINT32_CLASS, mxREAL); + qi = mxGetData(out[0]); + qj = mxGetData(out[1]); + + if (out[0]==NULL || out[1]==NULL) { + mexErrMsgTxt("Not enough space for the output matrix."); + } + + total = 0; + for (j=0; j 2) { + mexErrMsgTxt("Too many output arguments."); + } + + + /* get image size */ + + i = mxGetM(in[0]); + j = mxGetN(in[0]); + dim = mxGetData(in[0]); + nr = (int)dim[0]; + if (j>1 || i>1) { + nc = (int)dim[1]; + } else { + nc = nr; + } + np = nr * nc; + + + /* get neighbourhood size */ + i = mxGetM(in[1]); + j = mxGetN(in[1]); + dim = mxGetData(in[1]); + r_i = (int)dim[0]; + + if (j>1 || i>1) { + r_j = (int)dim[1]; + } else { + r_j = r_i; + } + + if (r_i<0) { r_i = 0; } + + if (r_j<0) { r_j = 0; } + + + + /* get sample rate */ + + if (nargin==3) { + + sample_rate = (mxGetM(in[2])==0) ? 1: mxGetScalar(in[2]); + + } else { + + sample_rate = 1; + + } + + /* prepare for random number generator */ + if (sample_rate<1) { + srand( (unsigned)time( NULL ) ); + + th_rand = (int)ceil((double)RAND_MAX * sample_rate); + no_sample = 0; + } else { + + sample_rate = 1; + th_rand = RAND_MAX; + no_sample = 1; + } + + + /* figure out neighbourhood size */ + + nb = (4*r_i) + (4*r_j)+1; + if (nb>np) { + nb = np; + } + nb = (int)ceil((double)nb * sample_rate); + +/*printf("nb=%d\n",nb);*/ + + /* intermediate data structure */ + + /* p = mxCalloc(np * (nb+1), sizeof(unsigned long));*/ + p = mxCalloc(np * (nb+1), sizeof(unsigned int)); + + if (p==NULL) { + + mexErrMsgTxt("Not enough space for my computation."); + + } + + + + /* computation */ + + total = 0; + for (j=0; j=nc) { b2 = nc-1; } + + + /* i range */ + /*a1 = i - r_i; + + if (a1<0) { a1 = 0; }*/ + a2 = i + r_i; + if (a2>=nr) { a2 = nr-1; } + + + /* number of more samples needed */ + + nsamp = nb - p[self]; + + /*if (nsamp<0) + {printf("nsamp=%d\n",nsamp);}*/ + k = 0; + t = b1; + s = i + 1; + + if (s>a2) { + s = i; + t = t + 1; + } + + + + while (ka2) { + t = t + 1; + if (i+j-t>=0) + {s = i+j-t;} + else + {s=i;} + } + } + else { + if (s==i+j-t) {s=i;} + else{ if (s==i && s+t-j=0) + {s = i+j-t;} + else + {s=i;} + } + + } + } + + } /* k */ + + total = total + p[self]; + } /* i */ + + } /* j */ + + + + /* i, j */ + + out[0] = mxCreateNumericMatrix(total, 1, mxUINT32_CLASS, mxREAL); + + out[1] = mxCreateNumericMatrix(np+1, 1, mxUINT32_CLASS, mxREAL); + + qi = mxGetData(out[0]); + + qj = mxGetData(out[1]); + + + if (out[0]==NULL || out[1]==NULL) { + + mexErrMsgTxt("Not enough space for the output matrix."); + + } + + + + total = 0; + + for (j=0; j1) + for j=i-1:-1:1 + Wpp{i}=[C{j,i}',Wpp{i}]; + end + end + if (i1) +% for j=i-1:-1:1 +% Wpp{i}=[sparse(p(i)*q(i),p(j)*q(j)),Wpp{i}]; +% end +% end +% if (i2 + for i=3:n + piqi=p(i)*q(i); + if i~=n + constraintMat=[constraintMat,[sparse(sum(p(1:i-2).*q(1:i-2)),piqi);-C{i-1,i};speye(piqi);sparse(pq-sum(p(2:i).*q(2:i)),piqi)]]; + else + constraintMat=[constraintMat,[sparse(sum(p(1:i-2).*q(1:i-2)),piqi);-C{i-1,i};speye(piqi)]]; + end + end + end + + % saving useful information + %subgrids, p and q + data.subgrid=subgrid; + data.p=p; + data.q=q; diff --git a/SD-VBS/common/toolbox/MultiNcut/discretisation.m b/SD-VBS/common/toolbox/MultiNcut/discretisation.m new file mode 100755 index 0000000..70b5650 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/discretisation.m @@ -0,0 +1,49 @@ +function [SegLabel,EigenVectors]=discretisation(EigenVectors,nr,nc) +% +% EigenvectorsDiscrete=discretisation(EigenVectors) +% +% Input: EigenVectors = continuous Ncut vector, size = ndata x nbEigenvectors +% Output EigenvectorsDiscrete = discrete Ncut vector, size = ndata x nbEigenvectors +% +% Timothee Cour, Stella Yu, Jianbo Shi, 2004 + +[n,k]=size(EigenVectors); + +vm = sqrt(sum(EigenVectors.*EigenVectors,2)); +EigenVectors = EigenVectors./repmat(vm,1,k); + +R=zeros(k); +R(:,1)=EigenVectors(1+round(rand(1)*(n-1)),:)'; +c=zeros(n,1); +for j=2:k + c=c+abs(EigenVectors*R(:,j-1)); + [minimum,i]=min(c); + R(:,j)=EigenVectors(i,:)'; +end + +lastObjectiveValue=0; +exitLoop=0; +nbIterationsDiscretisation = 0; +nbIterationsDiscretisationMax = 20;%voir +while exitLoop== 0 + nbIterationsDiscretisation = nbIterationsDiscretisation + 1 ; + EigenvectorsDiscrete = discretisationEigenVectorData(EigenVectors*R); + [U,S,V] = svd(EigenvectorsDiscrete'*EigenVectors,0); + NcutValue=2*(n-trace(S)); + + if abs(NcutValue-lastObjectiveValue) < eps | nbIterationsDiscretisation > nbIterationsDiscretisationMax + exitLoop=1; + else + lastObjectiveValue = NcutValue; + R=V*U'; + end +end + +%%%% + +SegLabel = zeros(nr,nc); +for j=1:size(EigenvectorsDiscrete,2), + SegLabel = SegLabel + j*reshape(EigenvectorsDiscrete(:,j),nr,nc); +end +EigenVectors = reshape(EigenVectors,nr,nc,size(EigenVectors,2)); + diff --git a/SD-VBS/common/toolbox/MultiNcut/discretisationEigenVectorData.m b/SD-VBS/common/toolbox/MultiNcut/discretisationEigenVectorData.m new file mode 100755 index 0000000..4626e3d --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/discretisationEigenVectorData.m @@ -0,0 +1,12 @@ +function Y = discretisationEigenVectorData(EigenVector) +% Y = discretisationEigenVectorData(EigenVector) +% +% discretizes previously rotated eigenvectors in discretisation +% Timothee Cour, Stella Yu, Jianbo Shi, 2004 + +[n,k]=size(EigenVector); + + +[Maximum,J]=max(EigenVector'); + +Y=sparse(1:n,J',1,n,k); diff --git a/SD-VBS/common/toolbox/MultiNcut/doog1.m b/SD-VBS/common/toolbox/MultiNcut/doog1.m new file mode 100755 index 0000000..dd8e87b --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/doog1.m @@ -0,0 +1,32 @@ +function H=doog1(sig,r,th,N); +% H=doog1(sig,r,th,N); + + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +phi=pi*th/180; +sigy=sig; +sigx=r*sig; +R=[cos(phi) -sin(phi); sin(phi) cos(phi)]; +C=R*diag([sigx,sigy])*R'; + +X=[x(:) y(:)]; + +Gb=gaussian(X,[0 0]',C); +Gb=reshape(Gb,N,N); + +m=R*[0 sig]'; + +a=1; +b=-1; + +% make odd-symmetric filter +Ga=gaussian(X,m/2,C); +Ga=reshape(Ga,N,N); +Gb=rot90(Ga,2); +H=a*Ga+b*Gb; + diff --git a/SD-VBS/common/toolbox/MultiNcut/doog2.m b/SD-VBS/common/toolbox/MultiNcut/doog2.m new file mode 100755 index 0000000..a0511cb --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/doog2.m @@ -0,0 +1,38 @@ +function G=doog2(sig,r,th,N); +% G=doog2(sig,r,th,N); +% Make difference of offset gaussians kernel +% theta is in degrees +% (see Malik & Perona, J. Opt. Soc. Amer., 1990) +% +% Example: +% >> imagesc(doog2(1,12,0,64,1)) +% >> colormap(gray) + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +phi=pi*th/180; +sigy=sig; +sigx=r*sig; +R=[cos(phi) -sin(phi); sin(phi) cos(phi)]; +C=R*diag([sigx,sigy])*R'; + +X=[x(:) y(:)]; + +Gb=gaussian(X,[0 0]',C); +Gb=reshape(Gb,N,N); + +m=R*[0 sig]'; +Ga=gaussian(X,m,C); +Ga=reshape(Ga,N,N); +Gc=rot90(Ga,2); + +a=-1; +b=2; +c=-1; + +G = a*Ga + b*Gb + c*Gc; + diff --git a/SD-VBS/common/toolbox/MultiNcut/eigSolve.m b/SD-VBS/common/toolbox/MultiNcut/eigSolve.m new file mode 100755 index 0000000..cfb64fc --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/eigSolve.m @@ -0,0 +1,5 @@ +function [eigenVectors,s]= eigSolve (multiWpp,ConstraintMat,data) + +[v,s] = quickNcutHardBiais2(multiWpp,ConstraintMat,data.dataGraphCut.nbEigenValues,data.dataGraphCut); +v=v(1:data.p(1)*data.q(1),:); +eigenVectors=reshape(v,data.p(1),data.q(1),data.dataGraphCut.nbEigenValues); diff --git a/SD-VBS/common/toolbox/MultiNcut/fft_filt_2.m b/SD-VBS/common/toolbox/MultiNcut/fft_filt_2.m new file mode 100755 index 0000000..9c84e96 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/fft_filt_2.m @@ -0,0 +1,29 @@ +function FI=fft_filt_2(V,FB,sf); +% FI=fft_filt_2(V,FB,sf); +% fft-based filtering +% requires image to be called "V" +% and filters to be in FB +% sf is the subsampling factor +% +% FI is the result + +[M1,M2,N3]=size(FB); +% prepare FFT of image for filtering +[N1,N2]=size(V); +I=zeros(size(V)+[M1-1 M2-1]); +I(1:N1,1:N2)=V; +N1s=length(1:sf:N1); +N2s=length(1:sf:N2); +IF=fft2(I); +FI=zeros(N1s,N2s,N3); + +% apply filters +for n=1:N3; + f=rot90(FB(:,:,n),2); + fF=fft2(f,N1+M1-1,N2+M2-1); + IfF=IF.*fF; + If=real(ifft2(IfF)); + If=If(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + FI(:,:,n)=If(1:sf:N1,1:sf:N2); +end + diff --git a/SD-VBS/common/toolbox/MultiNcut/gaussian.m b/SD-VBS/common/toolbox/MultiNcut/gaussian.m new file mode 100755 index 0000000..509b129 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/gaussian.m @@ -0,0 +1,31 @@ +function p=gaussian(x,m,C); +% p=gaussian(x,m,C); +% +% Evaluate the multi-variate density with mean vector m and covariance +% matrix C for the input vector x. +% +% p=gaussian(X,m,C); +% +% Vectorized version: Here X is a matrix of column vectors, and p is +% a vector of probabilities for each vector. + +d=length(m); + +if size(x,1)~=d + x=x'; +end +N=size(x,2); + +detC = det(C); +if rcond(C)0) { + ir[k] = mxGetIr(in[k]); + jc[k] = mxGetJc(in[k]); + } + } + + out[0] = mxCreateDoubleMatrix(m[1],1,mxREAL); + y = mxGetPr(out[0]); + + + y1 = mxCalloc(n[2], sizeof(double)); + y2 = mxCalloc(m[3], sizeof(double)); + y2bis = mxCalloc(m[3], sizeof(double)); + y3 = mxCalloc(m[1], sizeof(double)); + y4 = mxCalloc(m[1], sizeof(double)); + y5 = mxCalloc(n[2], sizeof(double)); + y5bis = mxCalloc(n[2], sizeof(double)); + y6 = mxCalloc(n[2], sizeof(double)); + + CSR_VecMult_CAB_double(m[2],n[2],pr[2],ir[2],jc[2],pr[0],y1); + CSR_VecTriangSlvLD_CAB_double(m[3],pr[3],ir[3],jc[3],y1,y2bis); + CSC_VecTriangSlvUD_CAB_double(m[3],pr[3],ir[3],jc[3],y2bis,y2); + for(k=0;k0) { + ir[k] = mxGetIr(in[k]); + jc[k] = mxGetJc(in[k]); + } + } + + out[0] = mxCreateDoubleMatrix(m[1],1,mxREAL); + y = mxGetPr(out[0]); + + + y1 = mxCalloc(n[2], sizeof(double)); + y2 = mxCalloc(m[3], sizeof(double)); + y2bis = mxCalloc(m[3], sizeof(double)); + y3 = mxCalloc(m[1], sizeof(double)); + y4 = mxCalloc(m[1], sizeof(double)); + y5 = mxCalloc(n[2], sizeof(double)); + y5bis = mxCalloc(n[2], sizeof(double)); + y6 = mxCalloc(n[2], sizeof(double)); + + CSR_VecMult_CAB_double(m[2],n[2],pr[2],ir[2],jc[2],pr[0],y1); + CSR_VecTriangSlvLD_CAB_double(m[3],pr[3],ir[3],jc[3],y1,y2bis); + CSC_VecTriangSlvUD_CAB_double(m[3],pr[3],ir[3],jc[3],y2bis,y2); + for(k=0;k1) { + mexErrMsgTxt("Too many output arguments"); + } + + /* get edgel information */ + nr = mxGetM(in[0]); + nc = mxGetN(in[0]); + if ( nr*nc ==0 || nr != mxGetM(in[1]) || nc != mxGetN(in[1]) ) { + mexErrMsgTxt("Edge magnitude and phase shall be of the same image size"); + } + emag = mxGetPr(in[0]); + ephase = mxGetPr(in[1]); + np = nr * nc; + + /*get subgrid information*/ + + tmp = mxGetData(in[5]); + nrSubgrid = (int)tmp[0]; + tmp = mxGetData(in[6]); + ncSubgrid = (int)tmp[0]; + + /* printf("nrSubgrid=%d\n",nrSubgrid); + printf("ncSubgrid=%d\n",ncSubgrid); */ + if (nrSubgrid* ncSubgrid != mxGetM(in[4])*mxGetN(in[4])) { + mexErrMsgTxt("Error in the size of the subgrid"); + } + subgrid = mxGetData(in[4]); + nW = nrSubgrid * ncSubgrid; + + /* get new index pair */ + if (!mxIsUint32(in[2]) | !mxIsUint32(in[3])) { + mexErrMsgTxt("Index pair shall be of type UINT32"); + } + if (mxGetM(in[3]) * mxGetN(in[3]) != nW + 1) { + mexErrMsgTxt("Wrong index representation"); + } + pi = mxGetData(in[2]); + pj = mxGetData(in[3]); + subpi = mxGetData(in[7]); + /*{printf("pi[50] = %d\n",pi[50]);} + {printf("subpi[5] = %d\n",subpi[5]);} + {printf("subpi[6] = %d\n",subpi[6]);} + {printf("subpi[4] = %d\n",subpi[4]);}*/ + + /* create output */ /*!!!!!!!!!!!!!!!!!!!!!!!changer taille output!!!!!!!!!!*/ + out[0] = mxCreateSparse(nW,nW,pj[nW],mxREAL); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough memory for the output matrix"); + } + w = mxGetPr(out[0]); + ir = mxGetIr(out[0]); + jc = mxGetJc(out[0]); + + /* find my sigma */ + if (nargin<9) { + sigma = 0; + for (k=0; ksigma) { sigma = emag[k]; } + } + sigma = sigma / 6; + printf("sigma = %6.5f",sigma); + } else { + sigma = mxGetScalar(in[8]); + } + a = 0.5 / (sigma * sigma); + + /* computation */ + total = 0; + + for (j=0; j= abs(dj)) { + slope = dj / di; + step = (iy>=jy) ? 1 : -1; + + iip1 = jy; + jjp1 = jx; + + + for (ii=0;ii maxori){ + maxori = z; + } + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + + /* sample in j direction */ + } else { + slope = di / dj; + step = (ix>=jx) ? 1: -1; + + jjp1 = jx; + iip1 = jy; + + + for (jj=0;jj maxori){ + maxori = z; + } + + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + } + + maxori = 0.5 * maxori; + maxori = exp(-maxori * maxori * a); + } + /*if (total<20) {printf("subpi[k] = %d\n",subpi[k]);}*/ + + ir[total] = (int)subpi[k]; +/*if (total<20) {printf("ir[total] = %d\n",ir[total]);}*/ + w[total] = maxori; + total = total + 1; + + } /* i */ + } /*j*/ + /*printf("total = %d\n",total);*/ + /*printf("ir[100] = %d\n",ir[100]);*/ + jc[nW] = total; +} diff --git a/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.dll b/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.dll new file mode 100755 index 0000000..f9b6bc9 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.dll differ diff --git a/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexa64 b/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexa64 new file mode 100755 index 0000000..9a2a3cc Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexa64 differ diff --git a/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexglx b/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexglx new file mode 100755 index 0000000..ec11a0e Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/multiAffinityic.mexglx differ diff --git a/SD-VBS/common/toolbox/MultiNcut/multiIntensityFirstLayer.c b/SD-VBS/common/toolbox/MultiNcut/multiIntensityFirstLayer.c new file mode 100755 index 0000000..0cae523 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/multiIntensityFirstLayer.c @@ -0,0 +1,126 @@ +/*================================================================ +* function w = intensityWppc(image,pi,pj,rMin,sigmaX,sigmaIntensite,valeurMinW ) +* Input: +* [pi,pj] = index pair representation for MALTAB sparse matrices +* Output: +* w = affinity with IC at [pi,pj] +* + +pixels i and j (corresponding to the sampling in pi,pj) are fully connected when d(i,j) <= rmin; + +% test sequence +f = synimg(10); +[i,j] = cimgnbmap(size(f),2); +[ex,ey,egx,egy] = quadedgep(f); +a = affinityic(ex,ey,egx,egy,i,j) +show_dist_w(f,a); + +* Stella X. Yu, Nov 19, 2001. +*=================================================================*/ + +# include "mex.h" +# include "math.h" + +void mexFunction( + int nargout, + mxArray *out[], + int nargin, + const mxArray *in[] +) +{ + /* declare variables */ + int nr, nc, np, total; + int i, j, k, ix, iy, jx, jy; + int *ir, *jc; + int squareDistance; + /* unsigned long *pi, *pj; */ +unsigned int *pi, *pj; + double *w; + + double temp,a1,a2,wij; + double rMin; + double sigmaX, sigmaIntensite,valeurMinW; + double *image; + + /* check argument */ + if (nargin<7) { + mexErrMsgTxt("Four input arguments required"); + } + if (nargout>1) { + mexErrMsgTxt("Too many output arguments"); + } + + /* get edgel information */ + nr = mxGetM(in[0]); + nc = mxGetN(in[0]); + np = nr * nc; + + image = mxGetPr(in[0]); + + + + /* get new index pair */ + if (!mxIsUint32(in[1]) | !mxIsUint32(in[2])) { + mexErrMsgTxt("Index pair shall be of type UINT32"); + } + if (mxGetM(in[2]) * mxGetN(in[2]) != np + 1) { + mexErrMsgTxt("Wrong index representation"); + } + pi = mxGetData(in[1]); + pj = mxGetData(in[2]); + + /* create output */ + out[0] = mxCreateSparse(np,np,pj[np],mxREAL); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough memory for the output matrix"); + } + w = mxGetPr(out[0]); + ir = mxGetIr(out[0]); + jc = mxGetJc(out[0]); + + rMin = mxGetScalar(in[3]); + sigmaX = mxGetScalar(in[4]); + sigmaIntensite= mxGetScalar(in[5]); + valeurMinW = mxGetScalar(in[6]); + + a1 = 1.0/ (sigmaX*sigmaX); + a2 = 1.0 / (sigmaIntensite*sigmaIntensite ); + + /* computation */ + total = 0; + for (j=0; j1) { + mexErrMsgTxt("Too many output arguments"); + } + + /* get edgel information */ + nr = mxGetM(in[0]); + nc = mxGetN(in[0]); + np = nr * nc; + /*printf("size: %d, %d, %d\n", nc, nr, np); */ + image = mxGetPr(in[0]); + + + /*get subgrid information*/ + + tmp = mxGetData(in[8]); + nrSubgrid = (int)tmp[0]; + + /* printf("image end = %f ", image[np-1]); */ + + tmp = mxGetData(in[9]); + ncSubgrid = (int)tmp[0]; + + if (nrSubgrid* ncSubgrid != mxGetM(in[7])*mxGetN(in[7])) { + mexErrMsgTxt("Error in the size of the subgrid"); + } + subgrid = mxGetData(in[7]); + nW = nrSubgrid * ncSubgrid; + + + + + /* get new index pair */ + if (!mxIsUint32(in[1]) | !mxIsUint32(in[2])) { + mexErrMsgTxt("Index pair shall be of type UINT32"); + } + if (mxGetM(in[2]) * mxGetN(in[2]) != nW + 1) { + mexErrMsgTxt("Wrong index representation"); + } + pi = mxGetData(in[1]); + pj = mxGetData(in[2]); + subpi = mxGetData(in[10]); + + /* create output */ + out[0] = mxCreateSparse(nW,nW,pj[nW],mxREAL); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough memory for the output matrix"); + } + + w = mxGetPr(out[0]); + ir = mxGetIr(out[0]); + jc = mxGetJc(out[0]); + + + rMin = mxGetScalar(in[3]); + sigmaX = mxGetScalar(in[4]); + sigmaIntensite= mxGetScalar(in[5]); + valeurMinW = mxGetScalar(in[6]); + + a1 = 1.0/ (sigmaX*sigmaX); + a2 = 1.0 / (sigmaIntensite*sigmaIntensite ); + + + + /* computation */ + total = 0; + for (j=0; jnp-1)) {printf("badddddd!");} + /* printf("t = %d\n",t); + printf("j = %d\n",j); */ + jc[j] = total; + jx = t / nr; /* col */ + jy = t % nr; /* row */ + /* printf("pj[j+1] = %d\n",pj[j+1]); */ + + for (k=pj[j]; k5000*5000 ) {printf("trouble! [%d,%d]\n",k,(int)subpi[k]);} */ + + w[total] = wij; + + total = total + 1; + + } /* i */ + + + } /* j */ + + jc[nW] = total; +} diff --git a/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.dll b/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.dll new file mode 100755 index 0000000..16146c1 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.dll differ diff --git a/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexa64 b/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexa64 new file mode 100755 index 0000000..525ca62 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexa64 differ diff --git a/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexglx b/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexglx new file mode 100755 index 0000000..a17fa03 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/multiIntensityWppc.mexglx differ diff --git a/SD-VBS/common/toolbox/MultiNcut/quadedgep2.m b/SD-VBS/common/toolbox/MultiNcut/quadedgep2.m new file mode 100755 index 0000000..5041377 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/quadedgep2.m @@ -0,0 +1,188 @@ +% function [xs,ys,gx,gy,par,threshold,mag,mage,g,FIe,FIo,mago] = quadedgep(I,par,threshold); +% Input: +% I = image +% par = vector for 4 parameters +% [number of filter orientations, number of scale, filter size, elongation] +% To use default values, put 0. +% threshold = threshold on edge strength +% Output: +% [x,y,gx,gy] = locations and gradients of an ordered list of edgels +% x,y could be horizontal or vertical or 45 between pixel sites +% but it is guaranteed that there [floor(y) + (floor(x)-1)*nr] +% is ordered and unique. In other words, each edgel has a unique pixel id. +% par = actual par used +% threshold = actual threshold used +% mag = edge magnitude +% mage = phase map +% g = gradient map at each pixel +% [FIe,FIo] = odd and even filter outputs +% mago = odd filter output of optimum orientation + +% Stella X. Yu, 2001 + +% This is the multi scale version of the filtering +% For the moment the parameters are defined by default. See line 34 + + +function [x,y,gx,gy,par,threshold,mag_s,mage,g,FIe,FIo,mago] = quadedgep2(I,par,data,threshold); + + +if nargin<4 | isempty(threshold), + threshold = 0.1; +end + +[r,c] = size(I); +def_par = [4,30,3]; + +display_on = 1; + +% take care of parameters, any missing value is substituted by a default value +if nargin<2 | isempty(par), + par = def_par; +end +% par(end+1:4)=0; +% par = par(:); +% j = (par>0); +% have_value = [ j, 1-j ]; +% j = 1; n_filter = have_value(j,:) * [par(j); def_par(j)]; +% j = 2; n_scale = have_value(j,:) * [par(j); def_par(j)]; +% j = 3; winsz = have_value(j,:) * [par(j); def_par(j)]; +% j = 4; enlong = have_value(j,:) * [par(j); def_par(j)]; + +n_ori = par(1); %if it doesn't work, par<-def_par + +winsz = par(2); +enlong = par(3); + +% always make filter size an odd number so that the results will not be skewed +j = winsz/2; +if not(j > fix(j) + 0.1), + winsz = winsz + 1; +end + +% filter the image with quadrature filters +if (isempty(data.W.scales)) + error ('no scales entered'); +end + +n_scale=length(data.W.scales); +filter_scales=data.W.scales; +% +% if strcmp(data.dataWpp.mode,'multiscale') +% n_scale=size((data.dataWpp.scales),2); +% filter_scales=data.dataWpp.scales; +% else +% filter_scales=data.dataWpp.scales(1); +% n_scale=1; +% end +% if n_scale>0&&strcmp(data.dataWpp.mode,'multiscale') +% if (~isempty(data.dataWpp.scales)) +% filter_scales=data.dataWpp.scales; +% else +% filter_scales=zeros(1,n_scale); +% +% for i=1:n_scale, +% filter_scales(i)=(sqrt(2))^(i-1); +% end +% data.dataWpp.scales=filter_scales; +% end +% else filter_scale=1; +% data.dataWpp.scales=filter_scales; +% end +% +% %%%%%%% juste pour que ca tourne +% if ~strcmp(data.dataWpp.mode,'multiscale') +% filter_scales=data.dataWpp.scales(1); +% n_scale=4; +% end +% %%%%%%%%%%%% + +FBo = make_filterbank_odd2(n_ori,filter_scales,winsz,enlong); +FBe = make_filterbank_even2(n_ori,filter_scales,winsz,enlong); +n = ceil(winsz/2); +f = [fliplr(I(:,2:n+1)), I, fliplr(I(:,c-n:c-1))]; +f = [flipud(f(2:n+1,:)); f; flipud(f(r-n:r-1,:))]; +FIo = fft_filt_2(f,FBo,1); +FIo = FIo(n+[1:r],n+[1:c],:); +FIe = fft_filt_2(f,FBe,1); +FIe = FIe(n+[1:r],n+[1:c],:); + +% compute the orientation energy and recover a smooth edge map +% pick up the maximum energy across scale and orientation +% even filter's output: as it is the second derivative, zero cross localize the edge +% odd filter's output: orientation + +[nr,nc,nb] = size(FIe); + +FIe = reshape(FIe, nr,nc,n_ori,length(filter_scales)); +FIo = reshape(FIo, nr,nc,n_ori,length(filter_scales)); + +mag_s = zeros(nr,nc,n_scale); +mag_a = zeros(nr,nc,n_ori); + +mage = zeros(nr,nc,n_scale); +mago = zeros(nr,nc,n_scale); +mage = zeros(nr,nc,n_scale); +mago = zeros(nr,nc,n_scale); + + + +for i = 1:n_scale, + mag_s(:,:,i) = sqrt(sum(FIo(:,:,:,i).^2,3)+sum(FIe(:,:,:,i).^2,3)); + mag_a = sqrt(FIo(:,:,:,i).^2+FIe(:,:,:,i).^2); + [tmp,max_id] = max(mag_a,[],3); + + base_size = nr * nc; + id = [1:base_size]'; + mage(:,:,i) = reshape(FIe(id+(max_id(:)-1)*base_size+(i-1)*base_size*n_ori),[nr,nc]); + mago(:,:,i) = reshape(FIo(id+(max_id(:)-1)*base_size+(i-1)*base_size*n_ori),[nr,nc]); + + mage(:,:,i) = (mage(:,:,i)>0) - (mage(:,:,i)<0); + + if display_on, + ori_incr=pi/n_ori; % to convert jshi's coords to conventional image xy + ori_offset=ori_incr/2; + theta = ori_offset+([1:n_ori]-1)*ori_incr; % orientation detectors + % [gx,gy] are image gradient in image xy coords, winner take all + + ori = theta(max_id); + ori = ori .* (mago(:,:,i)>0) + (ori + pi).*(mago(:,:,i)<0); + gy{i} = mag_s(:,:,i) .* cos(ori); + gx{i} = -mag_s(:,:,i) .* sin(ori); + g = cat(3,gx{i},gy{i}); + + % phase map: edges are where the phase changes + mag_th = max(max(mag_s(:,:,i))) * threshold; + eg = (mag_s(:,:,i)>mag_th); + h = eg & [(mage(2:r,:,i) ~= mage(1:r-1,:,i)); zeros(1,nc)]; + v = eg & [(mage(:,2:c,i) ~= mage(:,1:c-1,i)), zeros(nr,1)]; + [y{i},x{i}] = find(h | v); + k = y{i} + (x{i}-1) * nr; + h = h(k); + v = v(k); + y{i} = y{i} + h * 0.5; % i + x{i} = x{i} + v * 0.5; % j + t = h + v * nr; + gx{i} = g(k) + g(k+t); + k = k + (nr * nc); + gy{i} = g(k) + g(k+t); + +% figure(1); +% clf; +% imagesc(I);colormap(gray); +% hold on; +% quiver(x,y,gx,gy); hold off; +% title(sprintf('scale = %d, press return',i)); + + % pause; + 0; +else + x = []; + y = []; + gx = []; + gy =[]; + g= []; + end +end + + diff --git a/SD-VBS/common/toolbox/MultiNcut/quickNcutHardBiais2.m b/SD-VBS/common/toolbox/MultiNcut/quickNcutHardBiais2.m new file mode 100755 index 0000000..3ca1046 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/quickNcutHardBiais2.m @@ -0,0 +1,187 @@ +% function [v,s] = quickNcutHardBiais(W,U,nbEigenValues,dataGraphCut) +%ligne 35 : changement tim +%[v,s] = ncut(W,nbEigenValues,[],offset); +%devient : +%[v,s] = tim_ncut_rapide(W,nbEigenValues,[],offset); +%et eigs devient tim_eigs + +% Input: +% W = affinity matrix +% U = hard constraint matrix, could be a cell of partial grouping +% nbEigenValues = number of eigenvectors +% offset = regularization factor, default = 0 +% Output: +% v = eigenvector +% s = eigenvalue of (W,d), s.t. U' * x = 0. + +% call eigs using my own * operation + +% Stella X. Yu, Jan 2002. + +function [v,s] = quickNcutHardBiais2(W,U,nbEigenValues,dataGraphCut)%voir : rajouter sigma +n = size(W,1); +nbEigenValues = min(nbEigenValues,n); + +offset = dataGraphCut.offset; +%offset = 2; + +% degrees and regularization +d = sum(abs(W),2); +dr = 0.5 * (d - sum(W,2)); +d = d + offset * 2; +dr = dr + offset; +W = W + spdiags(dr,0,n,n); +clear dr + +% normalize +Dinvsqrt = 1./sqrt(d+eps); +P = spmtimesd(W,Dinvsqrt,Dinvsqrt); +clear W; + +% if max(max(P-P')) < 1e-10 %ou eps +% %S = sparse(1:n,1:n,0.5); +% P =max(P,P'); +% % P=S*(P+P'); +% %P=0.5*(P+P'); +% options.issym = 1; +% end +P = sparsifyc(P,dataGraphCut.valeurMin); +options.issym = 1; + +Ubar = spmtimesd(U,Dinvsqrt,[]); +%Ubar = sparsifyc(Ubar,dataGraphCut.valeurMin); %voir + +options.disp = dataGraphCut.verbose; +options.maxit = dataGraphCut.maxiterations; +options.tol = dataGraphCut.eigsErrorTolerance; + +options.v0 = ones(size(P,1),1);%voir + +options.p = max(35,2*nbEigenValues); %voir +options.p = min(options.p , n); + +% nouvelle idee : factorisation de Cholesky +C=Ubar'*Ubar; +%permutation = symamd(C); +%R = cholinc(C(permutation,permutation)); +t_chol_Ubar = cputime; +[R,ooo] = cholinc(C,'0'); +t_chol_Ubar = cputime - t_chol_Ubar; +%if error occurs, check if Ubar = sparsifyc(Ubar,dataGraphCut.valeurMin); +%sparsifies too much + + +% compute H = (Ubar'*Ubar)^(-1) +% t_inv_H = cputime; +% H = inv(sparsifyc(Ubar' * Ubar,dataGraphCut.valeurMin)); %changer +% t_inv_H = cputime - t_inv_H; +% H = sparsifyc(H,dataGraphCut.valeurMin); +% tEigs = cputime; +% if options.issym & max(max(H-H')) < 1e-10 +% [vbar,s,convergence] = tim_eigs(@mex_projection_inv_symmetric,n,nbEigenValues,'lm',options,triu(P),Ubar,triu(H)); +% else +% [vbar,s,convergence] = tim_eigs(@mex_projection_inv,n,nbEigenValues,'lm',options,P,Ubar,H); +% end +% tEigs = cputime - tEigs; +% + + + +R = sparsifyc(R,dataGraphCut.valeurMin); +tEigs = cputime; +if options.issym + [vbar,s,convergence] = tim_eigs(@mex_projection_QR_symmetric,n,nbEigenValues,'lm',options,tril(P),Ubar,R); +else + [vbar,s,convergence] = tim_eigs(@mex_projection_QR,n,nbEigenValues,'lm',options,P,Ubar,R); +end +tEigs = cputime - tEigs; + + +%afficheTexte(sprintf('\n\nTemps ecoule pendant eigs : %g',tEigs),dataGraphCut.verbose,2); +%afficheTexte(sprintf('\nTemps ecoule pendant chol(Ubar''*Ubar) : %g',t_chol_Ubar),dataGraphCut.verbose); +if convergence~=0 + afficheTexte(sprintf(' (Non-convergence)'),dataGraphCut.verbose); +end + + +%disp(sprintf('nnz(P) : %f\n',nnz(P))); +%disp(sprintf('nnz(Ubar) : %f\n',nnz(Ubar))); +%disp(sprintf('nnz(R) : %f\n',nnz(R))); +%disp(sprintf('nnz(global) : %f\n',nnz(P) + 4 * nnz(Ubar) + 4*nnz(R))); + + + +s = real(diag(s)); +[x,y] = sort(-s); +s = -x; +vbar = vbar(:,y); + + +v = spdiags(Dinvsqrt,0,n,n) * vbar; + +for i=1:size(v,2) + %v(:,i) = v(:,i) / max(abs(v(:,i))); + v(:,i) = (v(:,i) / norm(v(:,i)) )*norm(ones(n,1)); + if v(1,i)~=0 + v(:,i) = - v(:,i) * sign(v(1,i)); + end +end + +% % nouvelle idee : factorisation de Cholesky +% t_chol_Ubar = cputime; +% R = chol(Ubar' * Ubar); +% t_chol_Ubar = cputime - t_chol_Ubar; +% R = sparsifyc(R,dataGraphCut.valeurMin); +% tEigs = cputime; +% if options.issym +% [vbar,s,convergence] = tim_eigs(@mex_projection_QR_symmetric,n,nbEigenValues,'lm',options,triu(P),Ubar,R); +% else +% [vbar,s,convergence] = tim_eigs(@mex_projection_QR,n,nbEigenValues,'lm',options,P,Ubar,R); +% end +% tEigs = cputime - tEigs; + + +% % compute H = (Ubar'*Ubar)^(-1) +% t_inv_H = cputime; +% H = inv(sparsifyc(Ubar' * Ubar,dataGraphCut.valeurMin)); %changer +% t_inv_H = cputime - t_inv_H; +% H = sparsifyc(H,dataGraphCut.valeurMin); +% tEigs = cputime; +% if options.issym & max(max(H-H')) < 1e-10 +% [vbar,s,convergence] = tim_eigs(@mex_projection_inv_symmetric,n,nbEigenValues,'lm',options,triu(P),Ubar,triu(H)); +% else +% [vbar,s,convergence] = tim_eigs(@mex_projection_inv,n,nbEigenValues,'lm',options,P,Ubar,H); +% end +% tEigs = cputime - tEigs; + + + +% % idee de mon rapport... semble pas marcher car R = qr(Ubar,0) est plus +% % lent que H = inv(sparsifyc(Ubar' * Ubar,dataGraphCut.valeurMin)); +% t_qr_Ubar = cputime; +% R = qr(Ubar,0); +% t_qr_Ubar = cputime - t_qr_Ubar; +% R = sparsifyc(R,dataGraphCut.valeurMin); +% tEigs2 = cputime; +% if options.issym +% [vbar2,s2,convergence] = tim_eigs(@mex_projection_QR_symmetric,n,nbEigenValues,'lm',options,triu(P),Ubar,R); +% else +% [vbar2,s2,convergence] = tim_eigs(@mex_projection_QR,n,nbEigenValues,'lm',options,P,Ubar,R); +% end +% tEigs2 = cputime - tEigs2; + + + +% idee de Jianbo... semble pas marcher car on a besoin de prendre k maximal +% dans [A,S,B] = svds(Ubar,k); +% +% [A,S,B] = svds(Ubar,300); +% A = sparsifyc(A,dataGraphCut.valeurMin); +% tEigs = cputime; +% [vbar,s,convergence] = tim_eigs(@mex_projection_svd,n,nbEigenValues,'lm',options,P,A); + +% afficheTexte(sprintf('\ninv(H) : %g',t_inv_H),dataGraphCut.verbose); +% afficheTexte(sprintf('\n\nTemps ecoule pendant eigs : %g',tEigs2),dataGraphCut.verbose,2); +% afficheTexte(sprintf('\nqr(Ubar) : %g',t_qr_Ubar),dataGraphCut.verbose); +% disp(sprintf('nnz(H) : %f\n',nnz(H))); +% disp(sprintf('nnz(global) : %f\n',nnz(P) + 4 * nnz(Ubar) + 2*nnz(H))); diff --git a/SD-VBS/common/toolbox/MultiNcut/read_data.m b/SD-VBS/common/toolbox/MultiNcut/read_data.m new file mode 100755 index 0000000..c0929c0 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/read_data.m @@ -0,0 +1,13 @@ + +%fnames = dir('/home/jshi/Results_DLIB/SegLabl*.mat'); + +fnames = dir('/data/jshi/DLIB/Results/Results_DLIB/SegLabl*.mat'); + +for j=1:length(fnames), + cm = sprintf('load /data/jshi/DLIB/Results/Results_DLIB/%s',fnames(j).name); + disp(cm);eval(cm); +figure(1);imagesc(I); colormap(gray); axis image; +figure(2); imagesc(SegLabel); axis image; + +pause; +end diff --git a/SD-VBS/common/toolbox/MultiNcut/readimage.m b/SD-VBS/common/toolbox/MultiNcut/readimage.m new file mode 100755 index 0000000..e45fa19 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/readimage.m @@ -0,0 +1,15 @@ +function I = readimage(fn,maxSize); + +Io = imread(fn); +[nr,nc,nb] = size(Io); + +if nb>1, + I = rgb2gray(Io); +else + I= Io; +end + +%maxSize = 400; +if max(nr,nc) > maxSize, + I = imresize(I,maxSize/max(nr,nc)); +end diff --git a/SD-VBS/common/toolbox/MultiNcut/run_script.m b/SD-VBS/common/toolbox/MultiNcut/run_script.m new file mode 100755 index 0000000..3d2e8c6 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/run_script.m @@ -0,0 +1,60 @@ +%% set path for the MNcut code + +if 1, +% MNcutDir = '/home/jshi/Matlab/Toolbox/MultiNcut'; + MNcutDir = 'C:\qihuizhu\Checkout\Human\Source\MultiNcut_new\MultiNcut'; + path(path,MNcutDir); +end + +%% set the image input and output dir. +% imagedir = '/data/jshi/DLIB/image.cd'; +% imagedir = 'C:\qihuizhu\Checkout\Human\Source\Data\test'; +imagedir = 'C:\qihuizhu\Checkout\Human\Data\Current\baby_case5'; +% imageformat = 'ppm'; +imageformat = 'tif'; + +% OutputDir = '/home/jshi/Results_DLIB'; +% OutputDir = 'C:\qihuizhu\Checkout\Human\Source\Data\test'; +OutputDir = 'C:\qihuizhu\Checkout\Human\Result\Segmentation\MultiNcut_new_03.07'; + +a = dir(OutputDir); +if (length(a) == 0), + cm = sprintf('mkdir %s',OutputDir); + disp(cm); eval(cm); +end + +files = dir(sprintf('%s/*.%s',imagedir,imageformat)); + +%% image size definition +imageSize = 400; + +% for id =11:200, +for id = 1:length(files) + %for id = 19:19, + I=readimage(sprintf('%s/%s',imagedir,files(id).name),imageSize); + + num_segs = [10, 20]; + + tic + [SegLabel,eigenVectors,eigenValues]= MNcut(I,num_segs); + toc + + for j=1:size(SegLabel,3), + [gx,gy] = gradient(SegLabel(:,:,j)); + bw = (abs(gx)>0.1) + (abs(gy) > 0.1); + + figure(1);clf; J1=showmask(double(I),bw); imagesc(J1);axis image; + cm = sprintf('print -djpeg %s/file%.4d-%.2d.jpg',OutputDir,id,num_segs(j)); disp(cm);eval(cm); + + + figure(10);imagesc(SegLabel(:,:,j));axis image; + cm = sprintf('print -djpeg %s/Seg%.4d-%.2d.jpg',OutputDir,id,num_segs(j));disp(cm);eval(cm); + +% pause; + end + + fname = files(id).name; + %cm = sprintf('save %s/SegLabl%.4d.mat I SegLabel fname',OutputDir,id); disp(cm); eval(cm); + %cm = sprintf('save %s/SegEig%.4d.mat eigenVectors eigenValues',OutputDir,id);disp(cm); eval(cm); + +end diff --git a/SD-VBS/common/toolbox/MultiNcut/showmask.m b/SD-VBS/common/toolbox/MultiNcut/showmask.m new file mode 100755 index 0000000..6fd1142 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/showmask.m @@ -0,0 +1,65 @@ +% function RGB=showmask(V,M,hue); +% Input: +% V = image +% M = nonnegative mask +% hue = a number in [0,1], red,yellow,green,cyan,...,red +% a char, 'r','g','b','y','c','m' +% or a matrix of the same size of image +% eg. hue = mask1 * 0.7 + mask2 * 1; +% +% Output: +% RGB = an RGB image with V as shades and M as saturated hues +% If no output is required, this image is displayed. + +% Stella X. YU, 2000. Based on Jianbo Shi's version. + +function RGB=showmask(V,M,hue); + +if nargin<3 | isempty(hue), + hue = 0; +end +if ischar(hue), + switch hue, + case 'r', hue = 1.0; + case 'g', hue = 0.3; + case 'b', hue = 0.7; + case 'y', hue = 0.15; + case 'c', hue = 0.55; + case 'm', hue = 0.85; + end +end + + +V=V-min(V(:)); +V=V/max(V(:)); +V=.25+0.75*V; %brighten things up a bit + +M = double(M); +M = M-min(M(:)); +M = M/max(M(:)); + +H = hue+zeros(size(V)); +S = M; +RGB = hsv2rgb(H,S,V); + +if nargout>0, + return; +end + +hold off; image(RGB); axis('image'); +s = cell(1,2); +if isempty(inputname(1)), + s{1} = 'image'; +else + s{1} = inputname(1); +end +if isempty(inputname(2)), + s{2} = 'mask'; +else + s{2} = inputname(2); +end +title(sprintf('%s and colored %s',s{1},s{2})); + +if nargout==0, + clear RGB; +end diff --git a/SD-VBS/common/toolbox/MultiNcut/sparsifyc.c b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.c new file mode 100755 index 0000000..82fca98 --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.c @@ -0,0 +1,232 @@ +/*================================================================= +* syntax: SPMX = SPARSIFY(MX, THRES) +* +* SPARSIFY - sparsify the input matrix, i.e. ignore the values +* of the matrix which are below a threshold +* +* Input: - MX: m-by-n matrix (sparse or full) +* - THRES: threshold value (double) +* +* Output: - SPMX: m-by-n sparse matrix only with values +* whose absolut value is above the given threshold +* +* Written by Mirko Visontai (10/24/2003) +*=================================================================*/ + + +#include +#include "mex.h" + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + /* Declare variable */ + int i,m,n,nzmax,newnnz,col,processed,passed; + int starting_row_index, current_row_index, stopping_row_index; + double *in_pr,*in_pi,*out_pr,*out_pi; + int *in_ir,*in_jc,*out_ir,*out_jc; + double thres; + + /* Check for proper number of input and output arguments */ + if ((nlhs != 1) || (nrhs != 2)){ + mexErrMsgTxt("usage: SPMX = SPARSIFY(MX, THRES)."); + } + /* if matrix is complex threshold the norm of the numbers */ + if (mxIsComplex(prhs[0])){ + /* Check data type of input argument */ + if (mxIsSparse(prhs[0])){ + + /* read input */ + in_pr = mxGetPr(prhs[0]); + in_pi = mxGetPi(prhs[0]); + in_ir = mxGetIr(prhs[0]); + in_jc = mxGetJc(prhs[0]); + nzmax = mxGetNzmax(prhs[0]); + m = mxGetM(prhs[0]); + n = mxGetN(prhs[0]); + thres = mxGetScalar(prhs[1]); + + /* Count new nonzeros */ + newnnz=0; + for(i=0; ithres) {newnnz++;} + } + + if (newnnz>0){ + /* create output */ + plhs[0] = mxCreateSparse(m,n,newnnz,mxCOMPLEX); + if (plhs[0]==NULL) + mexErrMsgTxt("Could not allocate enough memory!\n"); + out_pr = mxGetPr(plhs[0]); + out_pi = mxGetPr(plhs[0]); + out_ir = mxGetIr(plhs[0]); + out_jc = mxGetJc(plhs[0]); + passed = 0; + out_jc[0] = 0; + for (col=0; col thres){ + + out_pr[passed]=in_pr[current_row_index]; + out_pi[passed]=in_pi[current_row_index]; + out_ir[passed]=in_ir[current_row_index]; + out_jc[col+1] = out_jc[col+1]+1; + passed++; + } + } + } + } + } + else{ + plhs[0] = mxCreateSparse(m,n,0,mxCOMPLEX); + } + } + else{ /* for full matrices */ + /* read input */ + in_pr = mxGetPr(prhs[0]); + in_pi = mxGetPr(prhs[0]); + m = mxGetM(prhs[0]); + n = mxGetN(prhs[0]); + thres = mxGetScalar(prhs[1]); + + /* Count new nonzeros */ + newnnz=0; + for(i=0; ithres) {newnnz++;} + } + + if (newnnz>0){ + /* create output */ + plhs[0] = mxCreateSparse(m,n,newnnz,mxCOMPLEX); + if (plhs[0]==NULL) + mexErrMsgTxt("Could not allocate enough memory!\n"); + out_pr = mxGetPr(plhs[0]); + out_pi = mxGetPi(plhs[0]); + out_ir = mxGetIr(plhs[0]); + out_jc = mxGetJc(plhs[0]); + passed = 0; + out_jc[0] = 0; + + for (col=0; col thres){ + + out_pr[passed]=in_pr[current_row_index+m*col]; + out_ir[passed]=current_row_index; + out_jc[col+1] = out_jc[col+1]+1; + passed++; + } + } + } + } + else{ + plhs[0] = mxCreateSparse(m,n,0,mxCOMPLEX); + } + } + } + else { + /* Check data type of input argument */ + if (mxIsSparse(prhs[0])){ + + /* read input */ + in_pr = mxGetPr(prhs[0]); + in_ir = mxGetIr(prhs[0]); + in_jc = mxGetJc(prhs[0]); + nzmax = mxGetNzmax(prhs[0]); + n = mxGetN(prhs[0]); + m = mxGetM(prhs[0]); + thres = mxGetScalar(prhs[1]); + + /* Count new nonzeros */ + newnnz=0; + for(i=0; ithres) {newnnz++;} + } + + if (newnnz>0){ + /* create output */ + plhs[0] = mxCreateSparse(m,n,newnnz,mxREAL); + if (plhs[0]==NULL) + mexErrMsgTxt("Could not allocate enough memory!\n"); + out_pr = mxGetPr(plhs[0]); + out_ir = mxGetIr(plhs[0]); + out_jc = mxGetJc(plhs[0]); + passed = 0; + out_jc[0] = 0; + for (col=0; colthres){ + out_pr[passed]=in_pr[current_row_index]; + out_ir[passed]=in_ir[current_row_index]; + out_jc[col+1] = out_jc[col+1]+1; + passed++; + } + } + } + } + } + else{ + plhs[0] = mxCreateSparse(m,n,0,mxREAL); + } + } + else{ /* for full matrices */ + /* read input */ + in_pr = mxGetPr(prhs[0]); + n = mxGetN(prhs[0]); + m = mxGetM(prhs[0]); + thres = mxGetScalar(prhs[1]); + + /* Count new nonzeros */ + newnnz=0; + for(i=0; ithres) {newnnz++;} + } + + if (newnnz>0){ + /* create output */ + plhs[0] = mxCreateSparse(m,n,newnnz,mxREAL); + if (plhs[0]==NULL) + mexErrMsgTxt("Could not allocate enough memory!\n"); + out_pr = mxGetPr(plhs[0]); + out_ir = mxGetIr(plhs[0]); + out_jc = mxGetJc(plhs[0]); + passed = 0; + out_jc[0] = 0; + + for (col=0; colthres){ + out_pr[passed]=in_pr[current_row_index+m*col]; + out_ir[passed]=current_row_index; + out_jc[col+1] = out_jc[col+1]+1; + passed++; + } + } + } + } + else{ + plhs[0] = mxCreateSparse(m,n,0,mxREAL); + } + } + } +} + diff --git a/SD-VBS/common/toolbox/MultiNcut/sparsifyc.dll b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.dll new file mode 100755 index 0000000..cf832a6 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.dll differ diff --git a/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexa64 b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexa64 new file mode 100755 index 0000000..2f5ed26 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexa64 differ diff --git a/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexglx b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexglx new file mode 100755 index 0000000..0ba41b6 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexglx differ diff --git a/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexmac b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexmac new file mode 100755 index 0000000..caa2306 Binary files /dev/null and b/SD-VBS/common/toolbox/MultiNcut/sparsifyc.mexmac differ diff --git a/SD-VBS/common/toolbox/MultiNcut/spmtimesd.c b/SD-VBS/common/toolbox/MultiNcut/spmtimesd.c new file mode 100755 index 0000000..a98dc0a --- /dev/null +++ b/SD-VBS/common/toolbox/MultiNcut/spmtimesd.c @@ -0,0 +1,141 @@ +/*================================================================ +* spmtimesd.c +* This routine computes a sparse matrix times a diagonal matrix +* whose diagonal entries are stored in a full vector. +* +* Examples: +* spmtimesd(m,d,[]) = diag(d) * m, +* spmtimesd(m,[],d) = m * diag(d) +* spmtimesd(m,d1,d2) = diag(d1) * m * diag(d2) +* m could be complex, but d is assumed real +* +* Stella X. Yu's first MEX function, Nov 9, 2001. + +% test sequence: + +m = 1000; +n = 2000; +a=sparse(rand(m,n)); +d1 = rand(m,1); +d2 = rand(n,1); +tic; b=spmtimesd(a,d1,d2); toc +tic; bb = spdiags(d1,0,m,m) * a * spdiags(d2,0,n,n); toc +e = (bb-b); +max(abs(e(:))) + +*=================================================================*/ + +# include "mex.h" + +void mexFunction( + int nargout, + mxArray *out[], + int nargin, + const mxArray *in[] +) +{ + /* declare variables */ + int i, j, k, m, n, nzmax, cmplx, xm, yn; + int *pir, *pjc, *qir, *qjc; + double *x, *y, *pr, *pi, *qr, *qi; + + /* check argument */ + if (nargin != 3) { + mexErrMsgTxt("Three input arguments required"); + } + if (nargout>1) { + mexErrMsgTxt("Too many output arguments."); + } + if (!(mxIsSparse(in[0]))) { + mexErrMsgTxt("Input argument #1 must be of type sparse"); + } + if ( mxIsSparse(in[1]) || mxIsSparse(in[2]) ) { + mexErrMsgTxt("Input argument #2 & #3 must be of type full"); + } + + /* computation starts */ + m = mxGetM(in[0]); + n = mxGetN(in[0]); + pr = mxGetPr(in[0]); + pi = mxGetPi(in[0]); + pir = mxGetIr(in[0]); + pjc = mxGetJc(in[0]); + + i = mxGetM(in[1]); + j = mxGetN(in[1]); + xm = ((i>j)? i: j); + + i = mxGetM(in[2]); + j = mxGetN(in[2]); + yn = ((i>j)? i: j); + + if ( xm>0 && xm != m) { + mexErrMsgTxt("Row multiplication dimension mismatch."); + } + if ( yn>0 && yn != n) { + mexErrMsgTxt("Column multiplication dimension mismatch."); + } + + + nzmax = mxGetNzmax(in[0]); + cmplx = (pi==NULL ? 0 : 1); + out[0] = mxCreateSparse(m,n,nzmax,cmplx); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough space for the output matrix."); + } + + qr = mxGetPr(out[0]); + qi = mxGetPi(out[0]); + qir = mxGetIr(out[0]); + qjc = mxGetJc(out[0]); + + /* left multiplication */ + x = mxGetPr(in[1]); + if (yn==0) { + for (j=0; j 3) + error('Too many output arguments.') +end + +% Process inputs and do error-checking +if isa(varargin{1},'double') + A = varargin{1}; + Amatrix = 1; +else + A = fcnchk(varargin{1}); + Amatrix = 0; +end + +isrealprob = 1; % isrealprob = isreal(A) & isreal(B) & isreal(sigma) +if Amatrix + isrealprob = isreal(A); +end + +issymA = 0; +if Amatrix + issymA = isequal(A,A'); +end + +if Amatrix + [m,n] = size(A); + if (m ~= n) + error('A must be a square matrix or a function which computes A*x.') + end +else + n = varargin{2}; + nstr = 'Size of problem, ''n'', must be a positive integer.'; + if ~isequal(size(n),[1,1]) | ~isreal(n) + error(nstr) + end + if (round(n) ~= n) + warning('MATLAB:eigs:NonIntegerSize',['%s\n ' ... + 'Rounding input size.'],nstr) + n = round(n); + end + if issparse(n) + n = full(n); + end +end + +Bnotthere = 0; +Bstr = sprintf(['Generalized matrix B must be the same size as A and' ... + ' either a symmetric positive (semi-)definite matrix or' ... + ' its Cholesky factor.']); +if (nargin < (3-Amatrix-Bnotthere)) + B = []; + Bnotthere = 1; +else + Bk = varargin{3-Amatrix-Bnotthere}; + if isempty(Bk) % allow eigs(A,[],k,sigma,opts); + B = Bk; + else + if isequal(size(Bk),[1,1]) & (n ~= 1) + B = []; + k = Bk; + Bnotthere = 1; + else % eigs(9,8,...) assumes A=9, B=8, ... NOT A=9, k=8, ... + B = Bk; + if ~isa(B,'double') | ~isequal(size(B),[n,n]) + error(Bstr) + end + isrealprob = isrealprob & isreal(B); + end + end +end + +if Amatrix & ((nargin - ~Bnotthere)>4) + error('Too many inputs.') +end + +if (nargin < (4-Amatrix-Bnotthere)) + k = min(n,6); +else + k = varargin{4-Amatrix-Bnotthere}; +end + +kstr = ['Number of eigenvalues requested, k, must be a' ... + ' positive integer <= n.']; +if ~isa(k,'double') | ~isequal(size(k),[1,1]) | ~isreal(k) | (k>n) + error(kstr) +end +if issparse(k) + k = full(k); +end +if (round(k) ~= k) + warning('MATLAB:eigs:NonIntegerEigQty',['%s\n ' ... + 'Rounding number of eigenvalues.'],kstr) + k = round(k); +end + +whchstr = sprintf(['Eigenvalue range sigma must be a valid 2-element string.']); +if (nargin < (5-Amatrix-Bnotthere)) + % default: eigs('LM') => ARPACK which='LM', sigma=0 + eigs_sigma = 'LM'; + whch = 'LM'; + sigma = 0; +else + eigs_sigma = varargin{5-Amatrix-Bnotthere}; + if isstr(eigs_sigma) + % eigs(string) => ARPACK which=string, sigma=0 + if ~isequal(size(eigs_sigma),[1,2]) + whchstr = [whchstr sprintf(['\nFor real symmetric A, the choices are ''%s'', ''%s'', ''%s'', ''%s'' or ''%s''.'], ... + 'LM','SM','LA','SA','BE')]; + whchstr = [whchstr sprintf(['\nFor non-symmetric or complex A, the choices are ''%s'', ''%s'', ''%s'', ''%s'', ''%s'' or ''%s''.\n'], ... + 'LM','SM','LR','SR','LI','SI')]; + error(whchstr) + end + eigs_sigma = upper(eigs_sigma); + if isequal(eigs_sigma,'SM') + % eigs('SM') => ARPACK which='LM', sigma=0 + whch = 'LM'; + else + % eigs(string), where string~='SM' => ARPACK which=string, sigma=0 + whch = eigs_sigma; + end + sigma = 0; + else + % eigs(scalar) => ARPACK which='LM', sigma=scalar + if ~isa(eigs_sigma,'double') | ~isequal(size(eigs_sigma),[1,1]) + error('Eigenvalue shift sigma must be a scalar.') + end + sigma = eigs_sigma; + if issparse(sigma) + sigma = full(sigma); + end + isrealprob = isrealprob & isreal(sigma); + whch = 'LM'; + end +end + +tol = eps; % ARPACK's minimum tolerance is eps/2 (DLAMCH's EPS) +maxit = []; +p = []; +info = int32(0); % use a random starting vector +display = 1; +cholB = 0; + +if (nargin >= (6-Amatrix-Bnotthere)) + opts = varargin{6-Amatrix-Bnotthere}; + if ~isa(opts,'struct') + error('Options argument must be a structure.') + end + + if isfield(opts,'issym') & ~Amatrix + issymA = opts.issym; + if (issymA ~= 0) & (issymA ~= 1) + error('opts.issym must be 0 or 1.') + end + end + + if isfield(opts,'isreal') & ~Amatrix + if (opts.isreal ~= 0) & (opts.isreal ~= 1) + error('opts.isreal must be 0 or 1.') + end + isrealprob = isrealprob & opts.isreal; + end + + if ~isempty(B) & (isfield(opts,'cholB') | isfield(opts,'permB')) + if isfield(opts,'cholB') + cholB = opts.cholB; + if (cholB ~= 0) & (cholB ~= 1) + error('opts.cholB must be 0 or 1.') + end + if isfield(opts,'permB') + if issparse(B) & cholB + permB = opts.permB; + if ~isequal(sort(permB),(1:n)) & ... + ~isequal(sort(permB),(1:n)') + error('opts.permB must be a permutation of 1:n.') + end + else + warning('MATLAB:eigs:IgnoredOptionPermB', ... + ['Ignoring opts.permB since B is not its sparse' ... + ' Cholesky factor.']) + end + else + permB = 1:n; + end + end + end + + if isfield(opts,'tol') + if ~isequal(size(opts.tol),[1,1]) | ~isreal(opts.tol) | (opts.tol<=0) + error(['Convergence tolerance opts.tol must be a strictly' ... + ' positive real scalar.']) + else + tol = full(opts.tol); + end + end + + if isfield(opts,'p') + p = opts.p; + pstr = ['Number of basis vectors opts.p must be a positive' ... + ' integer <= n.']; + if ~isequal(size(p),[1,1]) | ~isreal(p) | (p<=0) | (p>n) + error(pstr) + end + if issparse(p) + p = full(p); + end + if (round(p) ~= p) + warning('MATLAB:eigs:NonIntegerVecQty',['%s\n ' ... + 'Rounding number of basis vectors.'],pstr) + p = round(p); + end + end + + if isfield(opts,'maxit') + maxit = opts.maxit; + str = ['Maximum number of iterations opts.maxit must be' ... + ' a positive integer.']; + if ~isequal(size(maxit),[1,1]) | ~isreal(maxit) | (maxit<=0) + error(str) + end + if issparse(maxit) + maxit = full(maxit); + end + if (round(maxit) ~= maxit) + warning('MATLAB:eigs:NonIntegerIterationQty',['%s\n ' ... + 'Rounding number of iterations.'],str) + maxit = round(maxit); + end + end + + if isfield(opts,'v0') + if ~isequal(size(opts.v0),[n,1]) + error('Start vector opts.v0 must be n-by-1.') + end + if isrealprob + if ~isreal(opts.v0) + error(['Start vector opts.v0 must be real for real problems.']) + end + resid = full(opts.v0); + else + resid(1:2:(2*n-1),1) = full(real(opts.v0)); + resid(2:2:2*n,1) = full(imag(opts.v0)); + end + info = int32(1); % use resid as starting vector + end + + if isfield(opts,'disp') + display = opts.disp; + dispstr = 'Diagnostic level opts.disp must be an integer.'; + if (~isequal(size(display),[1,1])) | (~isreal(display)) | (display<0) + error(dispstr) + end + if (round(display) ~= display) + warning('MATLAB:eigs:NonIntegerDiagnosticLevel', ... + '%s\n Rounding diagnostic level.',dispstr) + display = round(display); + end + end + + if isfield(opts,'cheb') + warning('MATLAB:eigs:ObsoleteOptionCheb', ... + ['Ignoring polynomial acceleration opts.cheb' ... + ' (no longer an option).']); + end + if isfield(opts,'stagtol') + warning('MATLAB:eigs:ObsoleteOptionStagtol', ... + ['Ignoring stagnation tolerance opts.stagtol' ... + ' (no longer an option).']); + end + +end + +% Now we know issymA, isrealprob, cholB, and permB + +if isempty(p) + if isrealprob & ~issymA + p = min(max(2*k+1,20),n); + else + p = min(max(2*k,20),n); + end +end +if isempty(maxit) + maxit = max(300,ceil(2*n/max(p,1))); +end +if (info == int32(0)) + if isrealprob + resid = zeros(n,1); + else + resid = zeros(2*n,1); + end +end + +if ~isempty(B) % B must be symmetric (Hermitian) positive (semi-)definite + if cholB + if ~isequal(triu(B),B) + error(Bstr) + end + else + if ~isequal(B,B') + error(Bstr) + end + end +end + +useeig = 0; +if isrealprob & issymA + knstr = sprintf(['For real symmetric problems, must have number' ... + ' of eigenvalues k < n.\n']); +else + knstr = sprintf(['For nonsymmetric and complex problems, must have' ... + ' number of eigenvalues k < n-1.\n']); +end +if isempty(B) + knstr = [knstr sprintf(['Using eig(full(A)) instead.'])]; +else + knstr = [knstr sprintf(['Using eig(full(A),full(B)) instead.'])]; +end +if (k == 0) + useeig = 1; +end +if isrealprob & issymA + if (k > n-1) + if (n >= 6) + warning('MATLAB:eigs:TooManyRequestedEigsForRealSym', ... + '%s',knstr) + end + useeig = 1; + end +else + if (k > n-2) + if (n >= 7) + warning('MATLAB:eigs:TooManyRequestedEigsForComplexNonsym', ... + '%s',knstr) + end + useeig = 1; + end +end + +if isrealprob & issymA + if ~isreal(sigma) + error(['For real symmetric problems, eigenvalue shift sigma must' ... + ' be real.']) + end +else + if ~isrealprob & issymA & ~isreal(sigma) + warning('MATLAB:eigs:ComplexShiftForRealProblem', ... + ['Complex eigenvalue shift sigma on a Hermitian problem' ... + ' (all real eigenvalues).']) + end +end + +if isrealprob & issymA + if strcmp(whch,'LR') + whch = 'LA'; + warning('MATLAB:eigs:SigmaChangedToLA', ... + ['For real symmetric problems, sigma value ''LR''' ... + ' (Largest Real) is now ''LA'' (Largest Algebraic).']) + end + if strcmp(whch,'SR') + whch = 'SA'; + warning('MATLAB:eigs:SigmaChangedToSA', ... + ['For real symmetric problems, sigma value ''SR''' ... + ' (Smallest Real) is now ''SA'' (Smallest Algebraic).']) + end + if ~ismember(whch,{'LM', 'SM', 'LA', 'SA', 'BE'}) + whchstr = [whchstr sprintf(['\nFor real symmetric A, the choices are ''%s'', ''%s'', ''%s'', ''%s'' or ''%s''.'], ... + 'LM','SM','LA','SA','BE')]; + error(whchstr) + end +else + if strcmp(whch,'BE') + warning('MATLAB:eigs:SigmaChangedToLM', ... + ['Sigma value ''BE'' is now only available for real' ... + ' symmetric problems. Computing ''LM'' eigenvalues instead.']) + whch = 'LM'; + end + if ~ismember(whch,{'LM', 'SM', 'LR', 'SR', 'LI', 'SI'}) + whchstr = [whchstr sprintf(['\nFor non-symmetric or complex A, the choices are ''%s'', ''%s'', ''%s'', ''%s'', ''%s'' or ''%s''.\n'], ... + 'LM','SM','LR','SR','LI','SI')]; + error(whchstr) + end +end + +% Now have enough information to do early return on cases eigs does not handle +if useeig + if (nargout <= 1) + varargout{1} = eigs2(A,n,B,k,whch,sigma,cholB, ... + varargin{7-Amatrix-Bnotthere:end}); + else + [varargout{1},varargout{2}] = eigs2(A,n,B,k,whch,sigma,cholB, ... + varargin{7-Amatrix-Bnotthere:end}); + end + if (nargout == 3) + varargout{3} = 0; % flag indicates "convergence" + end + return +end + +if isrealprob & ~issymA + sigmar = real(sigma); + sigmai = imag(sigma); +end + +if isrealprob & issymA + if (p <= k) + error(['For real symmetric problems, must have number of' ... + ' basis vectors opts.p > k.']) + end +else + if (p <= k+1) + error(['For nonsymmetric and complex problems, must have number of' ... + ' basis vectors opts.p > k+1.']) + end +end + +if isequal(whch,'LM') & ~isequal(eigs_sigma,'LM') + % A*x = lambda*M*x, M symmetric (positive) semi-definite + % => OP = inv(A - sigma*M)*M and B = M + % => shift-and-invert mode + mode = 3; +elseif isempty(B) + % A*x = lambda*x + % => OP = A and B = I + mode = 1; +else % B is not empty + % Do not use mode=2. + % Use mode = 1 with OP = R'\(A*(R\x)) and B = I + % where R is B's upper triangular Cholesky factor: B = R'*R. + % Finally, V = R\V returns the actual generalized eigenvectors of A and B. + mode = 1; +end + +if cholB + pB = 0; + RB = B; + RBT = B'; +end + +if (mode == 3) & Amatrix % need lu(A-sigma*B) + if issparse(A) & (isempty(B) | issparse(B)) + if isempty(B) + AsB = A - sigma * speye(n); + else + if cholB + AsB = A - sigma * RBT * RB; + else + AsB = A - sigma * B; + end + end + [L,U,P,Q] = lu(AsB); + [perm,dummy] = find(Q); + else + if isempty(B) + AsB = A - sigma * eye(n); + else + if cholB + AsB = A - sigma * RBT * RB; + else + AsB = A - sigma * B; + end + end + [L,U,P] = lu(AsB); + end + dU = diag(U); + rcondestU = full(min(abs(dU)) / max(abs(dU))); + if (rcondestU < eps) + if isempty(B) + ds = sprintf(['(A-sigma*I) has small reciprocal condition' ... + ' estimate: %f\n'],rcondestU); + else + ds = sprintf(['(A-sigma*B) has small reciprocal condition' ... + ' estimate: %f\n'],rcondestU); + end + ds = [ds sprintf(['indicating that sigma is near an exact' ... + ' eigenvalue. The\nalgorithm may not converge unless' ... + ' you try a new value for sigma.\n'])]; + warning('MATLAB:eigs:SigmaNearExactEig',ds) + end +end + +if (mode == 1) & ~isempty(B) & ~cholB % need chol(B) + if issparse(B) + permB = symamd(B); + [RB,pB] = chol(B(permB,permB)); + else + [RB,pB] = chol(B); + end + if (pB == 0) + RBT = RB'; + else + error(Bstr) + end +end + +% Allocate outputs and ARPACK work variables +if isrealprob + if issymA % real and symmetric + prefix = 'ds'; + v = zeros(n,p); + ldv = int32(size(v,1)); + ipntr = int32(zeros(15,1)); + workd = zeros(n,3); + lworkl = p*(p+8); + workl = zeros(lworkl,1); + lworkl = int32(lworkl); + d = zeros(k,1); + else % real but not symmetric + prefix = 'dn'; + v = zeros(n,p); + ldv = int32(size(v,1)); + ipntr = int32(zeros(15,1)); + workd = zeros(n,3); + lworkl = 3*p*(p+2); + workl = zeros(lworkl,1); + lworkl = int32(lworkl); + workev = zeros(3*p,1); + d = zeros(k+1,1); + di = zeros(k+1,1); + end +else % complex + prefix = 'zn'; + zv = zeros(2*n*p,1); + ldv = int32(n); + ipntr = int32(zeros(15,1)); + workd = complex(zeros(n,3)); + zworkd = zeros(2*prod(size(workd)),1); + lworkl = 3*p^2+5*p; + workl = zeros(2*lworkl,1); + lworkl = int32(lworkl); + workev = zeros(2*2*p,1); + zd = zeros(2*(k+1),1); + rwork = zeros(p,1); +end + +ido = int32(0); % reverse communication parameter +if isempty(B) | (~isempty(B) & (mode == 1)) + bmat = 'I'; % standard eigenvalue problem +else + bmat = 'G'; % generalized eigenvalue problem +end +nev = int32(k); % number of eigenvalues requested +ncv = int32(p); % number of Lanczos vectors +iparam = int32(zeros(11,1)); +iparam([1 3 7]) = int32([1 maxit mode]); +select = int32(zeros(p,1)); + +cputms(1) = cputime - t0; % end timing pre-processing + +iter = 0; +ariter = 0; + + +%tim + + +indexArgumentsAfun = 7-Amatrix-Bnotthere:length(varargin); +nbArgumentsAfun = length(indexArgumentsAfun); +if nbArgumentsAfun >=1 + arguments_Afun = varargin{7-Amatrix-Bnotthere}; +end +if nbArgumentsAfun >=2 + arguments_Afun2 = varargin{7-Amatrix-Bnotthere+1}; +end +if nbArgumentsAfun >=3 + arguments_Afun3 = varargin{7-Amatrix-Bnotthere+2}; +end +%fin tim + + + +while (ido ~= 99) + + t0 = cputime; % start timing ARPACK calls **aupd + + if isrealprob + arpackc( [prefix 'aupd'], ido, ... + bmat, int32(n), whch, nev, tol, resid, ncv, ... + v, ldv, iparam, ipntr, workd, workl, lworkl, info); + else + zworkd(1:2:end-1) = real(workd); + zworkd(2:2:end) = imag(workd); + arpackc( 'znaupd', ido, ... + bmat, int32(n), whch, nev, tol, resid, ncv, zv, ... + ldv, iparam, ipntr, zworkd, workl, lworkl, ... + rwork, info ); + workd = reshape(complex(zworkd(1:2:end-1),zworkd(2:2:end)),[n,3]); + end + + if (info < 0) + es = sprintf('Error with ARPACK routine %saupd: info = %d',... + prefix,full(info)); + error(es) + end + + cputms(2) = cputms(2) + (cputime-t0); % end timing ARPACK calls **aupd + t0 = cputime; % start timing MATLAB OP(X) + + % Compute which columns of workd ipntr references + + + + + + %[row,col1] = ind2sub([n,3],double(ipntr(1))); + %tim + row = mod(double(ipntr(1))-1,n) + 1 ; + col1 = floor((double(ipntr(1))-1)/n) + 1; + + + if (row ~= 1) + str = sprintf(['ipntr(1)=%d does not refer to the start of a' ... + ' column of the %d-by-3 array workd.'],full(ipntr(1)),n); + error(str) + end + + + + %[row,col2] = ind2sub([n,3],double(ipntr(2))); + %tim + row = mod(double(ipntr(2))-1,n) + 1 ; + col2 = floor((double(ipntr(2))-1)/n) + 1; + + + + if (row ~= 1) + str = sprintf(['ipntr(2)=%d does not refer to the start of a' ... + ' column of the %d-by-3 array workd.'],full(ipntr(2)),n); + error(str) + end + if ~isempty(B) & (mode == 3) & (ido == 1) + [row,col3] = ind2sub([n,3],double(ipntr(3))); + if (row ~= 1) + str = sprintf(['ipntr(3)=%d does not refer to the start of a' ... + ' column of the %d-by-3 array workd.'],full(ipntr(3)),n); + error(str) + end + end + + switch (ido) + case {-1,1} + if Amatrix + if (mode == 1) + if isempty(B) + % OP = A*x + workd(:,col2) = A * workd(:,col1); + else + % OP = R'\(A*(R\x)) + if issparse(B) + workd(permB,col2) = RB \ workd(:,col1); + workd(:,col2) = A * workd(:,col2); + workd(:,col2) = RBT \ workd(permB,col2); + else + workd(:,col2) = RBT \ (A * (RB \ workd(:,col1))); + end + end + elseif (mode == 3) + if isempty(B) + if issparse(A) + workd(perm,col2) = U \ (L \ (P * workd(:,col1))); + else + workd(:,col2) = U \ (L \ (P * workd(:,col1))); + end + else % B is not empty + if (ido == -1) + if cholB + workd(:,col2) = RBT * (RB * workd(:,col1)); + else + workd(:,col2) = B * workd(:,col1); + end + if issparse(A) & issparse(B) + workd(perm,col2) = U \ (L \ (P * workd(:,col1))); + else + workd(:,col2) = U \ (L \ (P * workd(:,col1))); + end + elseif (ido == 1) + if issparse(A) & issparse(B) + workd(perm,col2) = U \ (L \ (P * workd(:,col3))); + else + workd(:,col2) = U \ (L \ (P * workd(:,col3))); + end + end + end + else % mode is not 1 or 3 + error(['Unknown mode returned from ' prefix 'aupd.']) + end + else % A is not a matrix + if (mode == 1) + if isempty(B) + % OP = A*x + %workd(:,col2) = feval(A,workd(:,col1),varargin{7-Amatrix-Bnotthere:end}); + + + + + + nombre_A_times_X = nombre_A_times_X + 1; + + + + pause(0); %voir + + if nbArgumentsAfun == 1 + workd(:,col2) = feval(A,workd(:,col1),arguments_Afun); + %workd(:,col2) = max(workd(:,col2),0); + elseif nbArgumentsAfun == 2 + workd(:,col2) = feval(A,workd(:,col1),arguments_Afun,arguments_Afun2); + elseif nbArgumentsAfun == 3 + workd(:,col2) = feval(A,workd(:,col1),arguments_Afun,arguments_Afun2,arguments_Afun3); + else + workd(:,col2) = feval(A,workd(:,col1),varargin{indexArgumentsAfun}); + end + %workd(:,col2) = tim_w_times_x_c(workd(:,col1),arguments_Afun); %slower + + else + % OP = R'\(A*(R\x)) + if issparse(B) + workd(permB,col2) = RB \ workd(:,col1); + workd(:,col2) = feval(A,workd(:,col2),arguments_Afun); + workd(:,col2) = RBT \ workd(permB,col2); + + else + workd(:,col2) = RBT \ feval(A,(RB\workd(:,col1)),arguments_Afun); + end + end + elseif (mode == 3) + if isempty(B) + workd(:,col2) = feval(A,workd(:,col1), arguments_Afun); + else + if (ido == -1) + if cholB + workd(:,col2) = RBT * (RB * workd(:,col1)); + else + workd(:,col2) = B * workd(:,col1); + end + workd(:,col2) = feval(A,workd(:,col2), arguments_Afun); + elseif (ido == 1) + workd(:,col2) = feval(A,workd(:,col3), arguments_Afun); + end + end + else % mode is not 1 or 3 + error(['Unknown mode returned from ' prefix 'aupd.']) + end + end % if Amatrix + case 2 + if (mode == 3) + if cholB + workd(:,col2) = RBT * (RB * workd(:,col1)); + else + workd(:,col2) = B * workd(:,col1); + end + else + error(['Unknown mode returned from ' prefix 'aupd.']) + end + case 3 + % setting iparam(1) = ishift = 1 ensures this never happens + warning('MATLAB:eigs:WorklShiftsUnsupported', ... + ['EIGS does not yet support computing the shifts in workl.' ... + ' Setting reverse communication parameter to 99 and returning.']) + ido = int32(99); + case 99 + otherwise + error(['Unknown value of reverse communication parameter' ... + ' returned from ' prefix 'aupd.']) + end + + cputms(3) = cputms(3) + (cputime-t0); % end timing MATLAB OP(X) + + %tim + if nombreIterations ~= double(ipntr(15)) + nombreIterations = double(ipntr(15)); + end + + if display >= 1 && display <=2 + iter = double(ipntr(15)); + if (iter > ariter) & (ido ~= 99) + ariter = iter; + ds = sprintf(['Iteration %d: a few Ritz values of the' ... + ' %d-by-%d matrix:'],iter,p,p); + disp(ds) + if isrealprob + if issymA + dispvec = [workl(double(ipntr(6))+(0:p-1))]; + if isequal(whch,'BE') + % roughly k Large eigenvalues and k Small eigenvalues + disp(dispvec(max(end-2*k+1,1):end)) + else + % k eigenvalues + disp(dispvec(max(end-k+1,1):end)) + end + else + dispvec = [complex(workl(double(ipntr(6))+(0:p-1)), ... + workl(double(ipntr(7))+(0:p-1)))]; + % k+1 eigenvalues (keep complex conjugate pairs together) + disp(dispvec(max(end-k,1):end)) + end + else + dispvec = [complex(workl(2*double(ipntr(6))-1+(0:2:2*(p-1))), ... + workl(2*double(ipntr(6))+(0:2:2*(p-1))))]; + disp(dispvec(max(end-k+1,1):end)) + end + end + end + +end % while (ido ~= 99) + +t0 = cputime; % start timing post-processing + +flag = 0; +if (info < 0) + es = sprintf('Error with ARPACK routine %saupd: info = %d',prefix,full(info)); + error(es) +else + if (nargout >= 2) + rvec = int32(1); % compute eigenvectors + else + rvec = int32(0); % do not compute eigenvectors + end + + if isrealprob + if issymA + arpackc( 'dseupd', rvec, 'A', select, ... + d, v, ldv, sigma, ... + bmat, int32(n), whch, nev, tol, resid, ncv, ... + v, ldv, iparam, ipntr, workd, workl, lworkl, info ); + if isequal(whch,'LM') | isequal(whch,'LA') + d = flipud(d); + if (rvec == 1) + v(:,1:k) = v(:,k:-1:1); + end + end + if ((isequal(whch,'SM') | isequal(whch,'SA')) & (rvec == 0)) + d = flipud(d); + end + else + arpackc( 'dneupd', rvec, 'A', select, ... + d, di, v, ldv, sigmar, sigmai, workev, ... + bmat, int32(n), whch, nev, tol, resid, ncv, ... + v, ldv, iparam, ipntr, workd, workl, lworkl, info ); + d = complex(d,di); + if rvec + d(k+1) = []; + else + zind = find(d == 0); + if isempty(zind) + d = d(k+1:-1:2); + else + d(max(zind)) = []; + d = flipud(d); + end + end + end + else + zsigma = [real(sigma); imag(sigma)]; + arpackc( 'zneupd', rvec, 'A', select, ... + zd, zv, ldv, zsigma, workev, ... + bmat, int32(n), whch, nev, tol, resid, ncv, zv, ... + ldv, iparam, ipntr, zworkd, workl, lworkl, ... + rwork, info ); + if issymA + d = zd(1:2:end-1); + else + d = complex(zd(1:2:end-1),zd(2:2:end)); + end + v = reshape(complex(zv(1:2:end-1),zv(2:2:end)),[n p]); + end + + if (info ~= 0) + es = ['Error with ARPACK routine ' prefix 'eupd: ']; + switch double(info) + case 2 + ss = sum(select); + if (ss < k) + es = [es ... + ' The logical variable select was only set with ' int2str(ss) ... + ' 1''s instead of nconv=' int2str(double(iparam(5))) ... + ' (k=' int2str(k) ').' ... + ' Please contact the ARPACK authors at arpack@caam.rice.edu.']; + else + es = [es ... + 'The LAPACK reordering routine ' prefix(1) ... + 'trsen did not return all ' int2str(k) ' eigenvalues.'] + end + case 1 + es = [es ... + 'The Schur form could not be reordered by the LAPACK routine ' ... + prefix(1) 'trsen.' ... + ' Please contact the ARPACK authors at arpack@caam.rice.edu.']; + case -14 + es = [es prefix ... + 'aupd did not find any eigenvalues to sufficient accuracy.']; + otherwise + es = [es sprintf('info = %d',full(info))]; + end + error(es) + else + nconv = double(iparam(5)); + if (nconv == 0) + if (nargout < 3) + warning('MATLAB:eigs:NoEigsConverged', ... + 'None of the %d requested eigenvalues converged.',k) + else + flag = 1; + end + elseif (nconv < k) + if (nargout < 3) + warning('MATLAB:eigs:NotAllEigsConverged', ... + 'Only %d of the %d requested eigenvalues converged.',nconv,k) + else + flag = 1; + end + end + end % if (info ~= 0) +end % if (info < 0) + +if (issymA) | (~isrealprob) + if (nargout <= 1) + if isrealprob + varargout{1} = d; + else + varargout{1} = d(k:-1:1,1); + end + else + varargout{1} = v(:,1:k); + varargout{2} = diag(d(1:k,1)); + if (nargout >= 3) + varargout{3} = flag; + end + end +else + if (nargout <= 1) + varargout{1} = d; + else + cplxd = find(di ~= 0); + % complex conjugate pairs of eigenvalues occur together + cplxd = cplxd(1:2:end); + v(:,[cplxd cplxd+1]) = [complex(v(:,cplxd),v(:,cplxd+1)) ... + complex(v(:,cplxd),-v(:,cplxd+1))]; + varargout{1} = v(:,1:k); + varargout{2} = diag(d); + if (nargout >= 3) + varargout{3} = flag; + end + end +end + +if (nargout >= 2) & (mode == 1) & ~isempty(B) + if issparse(B) + varargout{1}(permB,:) = RB \ varargout{1}; + else + varargout{1} = RB \ varargout{1}; + end +end + +cputms(4) = cputime-t0; % end timing post-processing + +cputms(5) = sum(cputms(1:4)); % total time + +if (display >= 2) %tim + if (mode == 1) + innerstr = sprintf(['Compute A*X:' ... + ' %f\n'],cputms(3)); + elseif (mode == 2) + innerstr = sprintf(['Compute A*X and solve B*X=Y for X:' ... + ' %f\n'],cputms(3)); + elseif (mode == 3) + if isempty(B) + innerstr = sprintf(['Solve (A-SIGMA*I)*X=Y for X:' ... + ' %f\n'],cputms(3)); + else + innerstr = sprintf(['Solve (A-SIGMA*B)*X=B*Y for X:' ... + ' %f\n'],cputms(3)); + end + end + if ((mode == 3) & (Amatrix)) + if isempty(B) + prepstr = sprintf(['Pre-processing, including lu(A-sigma*I):' ... + ' %f\n'],cputms(1)); + else + prepstr = sprintf(['Pre-processing, including lu(A-sigma*B):' ... + ' %f\n'],cputms(1)); + end + elseif ((mode == 2) & (~cholB)) + prepstr = sprintf(['Pre-processing, including chol(B):' ... + ' %f\n'],cputms(1)); + else + prepstr = sprintf(['Pre-processing:' ... + ' %f\n'],cputms(1)); + end + sstr = sprintf(['***********CPU Timing Results in seconds***********']); + ds = sprintf(['\n' sstr '\n' ... + prepstr ... + 'ARPACK''s %saupd: %f\n' ... + innerstr ... + 'Post-processing with ARPACK''s %seupd: %f\n' ... + '***************************************************\n' ... + 'Total: %f\n' ... + sstr '\n'], ... + prefix,cputms(2),prefix,cputms(4),cputms(5)); + disp(ds) + if nombre_A_times_X > 0 %tim + disp(sprintf('Number of iterations : %f\n',nombreIterations)); + disp(sprintf('Number of times A*X was computed : %f\n',nombre_A_times_X)); + disp(sprintf('Average time for A*X : %f\n',cputms(3)/nombre_A_times_X)); + end +end diff --git a/SD-VBS/common/toolbox/ikkjin/getANMS.m b/SD-VBS/common/toolbox/ikkjin/getANMS.m new file mode 100755 index 0000000..a40d50c --- /dev/null +++ b/SD-VBS/common/toolbox/ikkjin/getANMS.m @@ -0,0 +1,30 @@ +function [interestPnts]=getANMS(x, y, v, r, dataDir) +MAX_LIMIT=100000; +C_ROBUST=1; +r_sq=r^2; +points=[x y v]; +[n temp]=size(v); +[srtdV srtdVIdx]=sort(v,'descend'); +srtdPnts=points(srtdVIdx,:); + +interestPnts=zeros(0,3); + +suppressR=ones(n,1)*MAX_LIMIT; +supId=find(suppressR>r_sq); + +iter = 0; +while length(supId)>0 + + interestPnts=[interestPnts; srtdPnts(supId(1),:)]; + srtdPnts=srtdPnts(supId(2:end),:); + suppressR=suppressR(supId(2:end),:); + + suppressR=min(suppressR,... + (C_ROBUST*interestPnts(end,3)>=srtdPnts(:,3)).*... + ((srtdPnts(:,1)-interestPnts(end,1)).^2 + (srtdPnts(:,2)-interestPnts(end,2)).^2)... + +(C_ROBUST*interestPnts(end,3)r_sq); +end diff --git a/SD-VBS/common/toolbox/ikkjin/getImgGrad.m b/SD-VBS/common/toolbox/ikkjin/getImgGrad.m new file mode 100755 index 0000000..90fae28 --- /dev/null +++ b/SD-VBS/common/toolbox/ikkjin/getImgGrad.m @@ -0,0 +1,7 @@ +function Ig=getImgGrad(imgroi) +im = double(rgb2gray(imgroi)); +g1 = fspecial('gaussian', 9,1); % Gaussian with sigma_d +img1 = conv2(im,g1,'same'); % blur image with sigma_d +Ix = conv2(img1,[-1 0 1],'same'); % take x derivative +Iy = conv2(img1,[-1;0;1],'same'); % take y derivative +Ig=Ix.^2+Iy.^2; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/ikkjin/harris.m b/SD-VBS/common/toolbox/ikkjin/harris.m new file mode 100755 index 0000000..92a6543 --- /dev/null +++ b/SD-VBS/common/toolbox/ikkjin/harris.m @@ -0,0 +1,43 @@ + +% Sample code for detecting Harris corners, following +% Brown et al, CVPR 2005 +% by Alyosha Efros, so probably buggy... +% slightly modified by ikkjin + +function [x,y,v] = harris(imrgb); +[nr nc nb]=size(imrgb); +if nb==3 + im=rgb2gray(imrgb); +else + im=imrgb; +end + +im = im2double(im); +g1 = fspecial('gaussian', 9,1); % Gaussian with sigma_d +g2 = fspecial('gaussian', 11,1.5); % Gaussian with sigma_i +img1 = conv2(im,g1,'same'); % blur image with sigma_d +Ix = conv2(img1,[-1 0 1],'same'); % take x derivative +Iy = conv2(img1,[-1;0;1],'same'); % take y derivative + +% Compute elements of the Harris matrix H +%%% we can use blur instead of the summing window +Ix2 = conv2(Ix.*Ix,g2,'same'); +Iy2 = conv2(Iy.*Iy,g2,'same'); +IxIy = conv2(Ix.*Iy,g2,'same'); +R = (Ix2.*Iy2 - IxIy.*IxIy) ... % det(H) + ./ (Ix2 + Iy2 + eps); % trace(H) + epsilon + +% don't want corners close to image border +R([1:15, end-16:end], :) = 0; +R(:,[1:15,end-16:end]) = 0; + +% non-maxima supression within 3x3 windows +nonmax = inline('max(x)'); +Rmax = colfilt(R,[3 3],'sliding',nonmax); % find neighbrhood max +Rnm = R.*(R == Rmax); % supress non-max + +% extract all interest points +[y,x,v] = find(Rnm); + + + diff --git a/SD-VBS/common/toolbox/lagrcv/Makefile b/SD-VBS/common/toolbox/lagrcv/Makefile new file mode 100755 index 0000000..af7e8f5 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/Makefile @@ -0,0 +1,21 @@ +rm liblagrcv.a +gcc -c -fPIC lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a + +mex -O calcSubsampleAvgMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcSobelMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcImgBlurMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcOptFlowLKMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcResizedImgMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcTextureMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcGradientPyrMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcSobelPyrMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcTexturePyrMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcOptFlowLKPyrMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcOptFlowLKPyrMex2.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcOptFlowLKPyrWInitMex2.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcOptFlowLKPyrWInitMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O calcOptFlowLKPyrWInitSobelMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ +mex -O dummyMex.cc -L/u/ikkjin/Matlab/Toolbox/lagrcv -llagrcv -I/u/ikkjin/Matlab/Toolbox/lagrcv/ + diff --git a/SD-VBS/common/toolbox/lagrcv/README.sxw b/SD-VBS/common/toolbox/lagrcv/README.sxw new file mode 100755 index 0000000..9f20d14 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/README.sxw differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcGradientPyrMex.cc b/SD-VBS/common/toolbox/lagrcv/calcGradientPyrMex.cc new file mode 100755 index 0000000..d94130b --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcGradientPyrMex.cc @@ -0,0 +1,45 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [dxPye dyPyr] = + // calcGradientPyrMex(imagePyr) + + const int *cellDims = mxGetDimensions(prhs[0]); + double *image; + const mxArray* imgArray; + mxArray *dxArray, *dyArray; + double *dx, *dy; + const int *imdims; + + plhs[0] = mxCreateCellArray(1, cellDims); + plhs[1] = mxCreateCellArray(1, cellDims); + + for(int i=0; i +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [IBlur] = + // calcImgBlurMex(image) + + double *image, *retImg; + const int *imdims; + + image=(double*)mxGetPr(prhs[0]); + imdims = mxGetDimensions(prhs[0]); + + plhs[0] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + retImg=(double*)mxGetPr(plhs[0]); + + calcImgBlur(image, imdims[0], imdims[1], retImg); +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexa64 b/SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexa64 new file mode 100755 index 0000000..8f4bfea Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexa64 differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexglx b/SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexglx new file mode 100755 index 0000000..ade786d Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcImgBlurMex.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcOptFlowLKMex.cc b/SD-VBS/common/toolbox/lagrcv/calcOptFlowLKMex.cc new file mode 100755 index 0000000..e22af8b --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcOptFlowLKMex.cc @@ -0,0 +1,85 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +#ifndef MAX_LEVEL +# define MAX_LEVEL 5 +#endif +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [newFeaturePnt validFlag ] = + // calcOptFlowLKMex(I, Idx, Idy, J, c_xx, c_xy, c_yy, featurePnt, initialPnt, winSize, accuracy_th, max_iter) + // featurePnt 2xn int + // winSize c_level int + // c_xx c_xy c_yy: image size double* + // image must be double + + + const int* imdims; + const int *nFeatures; + double *imgI, *iDx, *iDy, *imgJ, *c_xx, *c_xy, *c_yy; + double *fPnt, *initPnt, *newFPnt; + char* valid; + double accuracy_th; + int winSize, max_iter; + + if (nrhs > 10) { + accuracy_th=(double)mxGetScalar(prhs[10]); + max_iter=(int)mxGetScalar(prhs[11]); + } + + winSize = (int)mxGetScalar(prhs[9]); + initPnt=(double*)mxGetPr(prhs[8]); + fPnt=(double*)mxGetPr(prhs[7]); + c_xx=(double*)mxGetPr(prhs[6]); + c_xy=(double*)mxGetPr(prhs[5]); + c_yy=(double*)mxGetPr(prhs[4]); + imgJ=(double*)mxGetPr(prhs[3]); + iDy=(double*)mxGetPr(prhs[2]); + iDx=(double*)mxGetPr(prhs[1]); + imgI=(double*)mxGetPr(prhs[0]); + nFeatures=mxGetDimensions(prhs[7]); + imdims=mxGetDimensions(prhs[0]); + + plhs[0] = mxCreateNumericMatrix(nFeatures[0], nFeatures[1], mxDOUBLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericMatrix(1, nFeatures[1], mxUINT8_CLASS, mxREAL); + + newFPnt = (double*)mxGetPr(plhs[0]); + valid = (char*)mxGetPr(plhs[1]); + + //idx convert from matlab to c + for(int i=0; i10){ + calcLKTrack( imgI, iDx, iDy, imgJ, imdims, + c_xx, c_xy, c_yy, + fPnt, initPnt, nFeatures[1], winSize, + newFPnt, valid, accuracy_th, max_iter); + }else{ + calcLKTrack( imgI, iDx, iDy, imgJ, imdims, + c_xx, c_xy, c_yy, + fPnt, initPnt, nFeatures[1], winSize, + newFPnt, valid); + } + //idx convert from matlab to c + for(int i=0; i +#include + +#ifndef MAX_LEVEL +# define MAX_LEVEL 5 +#endif +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [newFeaturePnt validFlag ] = + // calcOptFlowPyrLKMex(Ipyr, IdxPyr, IdyPyr, Jpyr, featurePnt, winSize, accuracy_th, max_iter) + // Ipyr, IdxPyr, IdyPyr, Jpyr: levelx1 size cell. + // featurePnt 2xn int + // winSize c_level int + // c_xx c_xy c_yy: image size double* + // image must be double + + + const mxArray* imgArray; + int* imdims; + const int *curImgDims; + const int *nFeatures; + //double **iP, **iDxP, **iDyP, **jP; + double *iP[MAX_LEVEL], *iDxP[MAX_LEVEL], *iDyP[MAX_LEVEL], *jP[MAX_LEVEL]; + double *fPnt, *newFPnt; + char* valid; + double accuracy_th; + int winSize, max_iter; + const int *cellDims = mxGetDimensions(prhs[0]); + + if (nrhs > 6) { + accuracy_th=(double)mxGetScalar(prhs[6]); + max_iter=(int)mxGetScalar(prhs[7]); + } + + winSize = (int)mxGetScalar(prhs[5]); + fPnt=(double*)mxGetPr(prhs[4]); + nFeatures=mxGetDimensions(prhs[4]); + + imdims=(int*)malloc(sizeof(int)*cellDims[0]*2); + //iP=(double**)malloc(sizeof(double*)*cellDims[0]); + //iDxP=(double**)malloc(sizeof(double*)*cellDims[0]); + //iDyP=(double**)malloc(sizeof(double*)*cellDims[0]); + //jP=(double**)malloc(sizeof(double*)*cellDims[0]); + + for(int i=0; i6){ + calcPyrLKTrack( iP, iDxP, iDyP, jP, imdims, cellDims[0], fPnt, nFeatures[1], winSize, + newFPnt, valid, accuracy_th, max_iter); + }else{ + calcPyrLKTrack( iP, iDxP, iDyP, jP, imdims, cellDims[0], fPnt, nFeatures[1], winSize, + newFPnt, valid); + } + //idx convert from matlab to c + for(int i=0; i +#include + +#ifndef MAX_LEVEL +# define MAX_LEVEL 5 +#endif +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [newFeaturePnt validFlag ] = + // calcOptFlowPyrLKMex(Ipyr, IdxPyr, IdyPyr, Jpyr, featurePnt, winSize, accuracy_th, max_iter) + // Ipyr, IdxPyr, IdyPyr, Jpyr: levelx1 size cell. + // featurePnt 2xn int + // winSize c_level int + // c_xx c_xy c_yy: image size double* + // image must be double + + + const mxArray* imgArray; + int* imdims; + const int *curImgDims; + const int *nFeatures; + //double **iP, **iDxP, **iDyP, **jP; + double *iP[MAX_LEVEL], *iDxP[MAX_LEVEL], *iDyP[MAX_LEVEL], *jP[MAX_LEVEL]; + double *fPnt, *newFPnt; + char* valid; + double accuracy_th; + int winSize, max_iter; + const int *cellDims = mxGetDimensions(prhs[0]); + + if (nrhs > 6) { + accuracy_th=(double)mxGetScalar(prhs[6]); + max_iter=(int)mxGetScalar(prhs[7]); + } + + winSize = (int)mxGetScalar(prhs[5]); + fPnt=(double*)mxGetPr(prhs[4]); + nFeatures=mxGetDimensions(prhs[4]); + + imdims=(int*)malloc(sizeof(int)*cellDims[0]*2); + //iP=(double**)malloc(sizeof(double*)*cellDims[0]); + //iDxP=(double**)malloc(sizeof(double*)*cellDims[0]); + //iDyP=(double**)malloc(sizeof(double*)*cellDims[0]); + //jP=(double**)malloc(sizeof(double*)*cellDims[0]); + + for(int i=0; i6){ + calcPyrLKTrack( iP, iDxP, iDyP, jP, imdims, cellDims[0], fPnt, nFeatures[1], winSize, + newFPnt, valid, accuracy_th, max_iter); + }else{ + calcPyrLKTrack( iP, iDxP, iDyP, jP, imdims, cellDims[0], fPnt, nFeatures[1], winSize, + newFPnt, valid); + } + //idx convert from matlab to c + for(int i=0; i +#include + +#ifndef MAX_LEVEL +# define MAX_LEVEL 5 +#endif +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [newFeaturePnt validFlag ] = + // calcOptFlowLKPyrWInitMex2(Ipyr, IdxPyr, IdyPyr, Jpyr, featurePnt, winSize, accuracy_th, max_iter, initFPnt) + // Ipyr, IdxPyr, IdyPyr, Jpyr: levelx1 size cell. + // featurePnt 2xn int + // winSize c_level int + // c_xx c_xy c_yy: image size double* + // image must be double + + + const mxArray* imgArray; + int* imdims; + const int *curImgDims; + const int *nFeatures; + double *iP[MAX_LEVEL], *iDxP[MAX_LEVEL], *iDyP[MAX_LEVEL], *jP[MAX_LEVEL]; + double *fPnt, *newFPnt, *initFPnt; + char* valid; + double accuracy_th; + int winSize, max_iter; + const int *cellDims = mxGetDimensions(prhs[0]); + + accuracy_th=(double)mxGetScalar(prhs[6]); + max_iter=(int)mxGetScalar(prhs[7]); + initFPnt=(double*)mxGetPr(prhs[8]); + + winSize = (int)mxGetScalar(prhs[5]); + fPnt=(double*)mxGetPr(prhs[4]); + nFeatures=mxGetDimensions(prhs[4]); + + imdims=(int*)malloc(sizeof(int)*cellDims[0]*2); + + for(int i=0; i +#include + +#ifndef MAX_LEVEL +# define MAX_LEVEL 5 +#endif +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [newFeaturePnt validFlag ] = + // calcOptFlowLKPyrWInitMex2(Ipyr, IdxPyr, IdyPyr, Jpyr, featurePnt, winSize, accuracy_th, max_iter, initFPnt) + // Ipyr, IdxPyr, IdyPyr, Jpyr: levelx1 size cell. + // featurePnt 2xn int + // winSize c_level int + // c_xx c_xy c_yy: image size double* + // image must be double + + + const mxArray* imgArray; + int* imdims; + const int *curImgDims; + const int *nFeatures; + double *iP[MAX_LEVEL], *iDxP[MAX_LEVEL], *iDyP[MAX_LEVEL], *jP[MAX_LEVEL]; + double *fPnt, *newFPnt, *initFPnt; + char* valid; + double accuracy_th; + int winSize, max_iter; + const int *cellDims = mxGetDimensions(prhs[0]); + + accuracy_th=(double)mxGetScalar(prhs[6]); + max_iter=(int)mxGetScalar(prhs[7]); + initFPnt=(double*)mxGetPr(prhs[8]); + + winSize = (int)mxGetScalar(prhs[5]); + fPnt=(double*)mxGetPr(prhs[4]); + nFeatures=mxGetDimensions(prhs[4]); + + imdims=(int*)malloc(sizeof(int)*cellDims[0]*2); + + for(int i=0; i +#include + +#ifndef MAX_LEVEL +# define MAX_LEVEL 5 +#endif +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [newFeaturePnt validFlag ] = + // calcOptFlowLKPyrWInitMex2(Ipyr, Jpyr, featurePnt, winSize, accuracy_th, max_iter, initFPnt) + // Ipyr, IdxPyr, IdyPyr, Jpyr: levelx1 size cell. + // featurePnt 2xn int + // winSize c_level int + // c_xx c_xy c_yy: image size double* + // image must be double + + + const mxArray* imgArray; + int* imdims; + const int *curImgDims; + const int *nFeatures; + double *iP[MAX_LEVEL], *jP[MAX_LEVEL]; + double *fPnt, *newFPnt, *initFPnt; + char* valid; + double accuracy_th; + int winSize, max_iter; + const int *cellDims = mxGetDimensions(prhs[0]); + + accuracy_th=(double)mxGetScalar(prhs[4]); + max_iter=(int)mxGetScalar(prhs[5]); + initFPnt=(double*)mxGetPr(prhs[6]); + + winSize = (int)mxGetScalar(prhs[3]); + fPnt=(double*)mxGetPr(prhs[2]); + nFeatures=mxGetDimensions(prhs[2]); + + imdims=(int*)malloc(sizeof(int)*cellDims[0]*2); + + for(int i=0; i +#include + +#define WIN_SIZE 10 +#define PYR_LEVELS 3 + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ newpoints status pyr1 ] = + // calcOpticalFlowPyrLK(im1,im2,oldpoints,pyr1) + // images must be single-channel, 8-bit + // DO NOT PASS SAME IMAGE IN TWICE! + + char *im1_ptr = (char*)mxGetPr(prhs[0]); + char *im2_ptr = (char*)mxGetPr(prhs[1]); + const int *imdims = mxGetDimensions(prhs[0]); + int flags = 0; + int max_iter; + + if(nrhs>3){ + max_iter=(int)mxGetScalar(prhs[3]); + }else{ + max_iter=20; + } + + /* images */ + IplImage *im1 = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + IplImage *im2 = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + im1->imageData = im1_ptr; + im2->imageData = im2_ptr; + + /* coordinate arrays */ + CvPoint2D32f *oldpoints = (CvPoint2D32f*)mxGetPr(prhs[2]); + const int *pointsdim = mxGetDimensions(prhs[2]); + int npoints = pointsdim[1]; + plhs[0] = mxCreateNumericMatrix(2, npoints, mxSINGLE_CLASS, mxREAL); + CvPoint2D32f *newpoints = (CvPoint2D32f*)mxGetPr(plhs[0]); + + /* status array */ + plhs[1] = mxCreateNumericMatrix(1, npoints, mxUINT8_CLASS, mxREAL); + char *status = (char*)mxGetPr(plhs[1]); + + cvCalcOpticalFlowLK(im1, im2, + cvSize(WIN_SIZE, WIN_SIZE), velx, vely + status, + NULL, + cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,max_iter,0.03), + flags + ); +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.cc b/SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.cc new file mode 100755 index 0000000..d4be5ff --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.cc @@ -0,0 +1,77 @@ + +/* compile with + mex calcOpticalFlowPyrLK.cc -I/usr/local/opencv/include -L/usr/local/opencv/lib -lcxcore -lcv +*/ + +#include "mex.h" +#include "opencv/cv.h" +#include "opencv/highgui.h" +#include +#include + +#define WIN_SIZE 8 +#define PYR_LEVELS 3 + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ newpoints status pyr1 ] = + // calcOpticalFlowPyrLK(im1,im2,oldpoints,pyr1) + // images must be single-channel, 8-bit + // DO NOT PASS SAME IMAGE IN TWICE! + + char *im1_ptr = (char*)mxGetPr(prhs[0]); + char *im2_ptr = (char*)mxGetPr(prhs[1]); + const int *imdims = mxGetDimensions(prhs[0]); + IplImage *pyr1 = 0, *pyr2 = 0; + int flags = 0; + bool clearPyr1 = false; + int max_iter; + + max_iter=30; + + /* images */ + IplImage *im1 = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + IplImage *im2 = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + im1->imageData = im1_ptr; + im2->imageData = im2_ptr; + + /* allocate pyramids */ + pyr1 = cvCreateImageHeader(cvSize(imdims[0],imdims[1]), IPL_DEPTH_8U, 1); + pyr2 = cvCreateImageHeader(cvSize(imdims[0],imdims[1]), IPL_DEPTH_8U, 1); + // reuse pyramid if given + if (nrhs > 3) { + pyr1->imageData = (char*)mxGetPr(prhs[3]); + flags |= CV_LKFLOW_PYR_A_READY; + } else { + clearPyr1 = true; + cvCreateData(pyr1); + } + + // pyr2 will be reused, so allocate in return value + plhs[2] = mxCreateNumericMatrix(imdims[0], imdims[1], mxUINT8_CLASS, mxREAL); + pyr2->imageData = (char*)mxGetPr(plhs[2]); + + /* coordinate arrays */ + CvPoint2D32f *oldpoints = (CvPoint2D32f*)mxGetPr(prhs[2]); + const int *pointsdim = mxGetDimensions(prhs[2]); + int npoints = pointsdim[1]; + plhs[0] = mxCreateNumericMatrix(2, npoints, mxSINGLE_CLASS, mxREAL); + CvPoint2D32f *newpoints = (CvPoint2D32f*)mxGetPr(plhs[0]); + + /* status array */ + plhs[1] = mxCreateNumericMatrix(1, npoints, mxUINT8_CLASS, mxREAL); + char *status = (char*)mxGetPr(plhs[1]); + + cvCalcOpticalFlowPyrLK(im1, im2, + pyr1, pyr2, + oldpoints, newpoints, npoints, + cvSize(WIN_SIZE, WIN_SIZE), PYR_LEVELS, + status, + NULL, + cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,max_iter,0.03), + flags + ); + if (clearPyr1) + cvReleaseImage(&pyr1); +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.mexglx b/SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.mexglx new file mode 100755 index 0000000..ad5d0e9 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcOpticalFlowPyrLK.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.cc b/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.cc new file mode 100755 index 0000000..9c1aed7 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.cc @@ -0,0 +1,33 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [dxPye dyPyr] = + // calcGradientPyrMex(imagePyr) + + double *image, *retImg; + int newSizeY, newSizeX; + const int *imdims; + + image=(double*)mxGetPr(prhs[0]); + imdims = mxGetDimensions(prhs[0]); + + newSizeY=(imdims[0]+1)/2; + newSizeX=(imdims[1]+1)/2; + plhs[0] = mxCreateNumericMatrix(newSizeY, newSizeX, mxDOUBLE_CLASS, mxREAL); + retImg=(double*)mxGetPr(plhs[0]); + + calcImgResize(image, imdims[0], imdims[1], retImg, newSizeY, newSizeX); +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexa64 b/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexa64 new file mode 100755 index 0000000..0f66793 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexa64 differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexglx b/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexglx new file mode 100755 index 0000000..192ea5a Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcResizedImgMex.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcSobelMex.cc b/SD-VBS/common/toolbox/lagrcv/calcSobelMex.cc new file mode 100755 index 0000000..8667ec4 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcSobelMex.cc @@ -0,0 +1,32 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [dx dy] = + // calcSobelMex(image) + + double *image, *dx, *dy; + const int *imdims; + + image=(double*)mxGetPr(prhs[0]); + imdims = mxGetDimensions(prhs[0]); + + plhs[0] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + dx=(double*)mxGetPr(plhs[0]); + dy=(double*)mxGetPr(plhs[1]); + + calcSobel(image, imdims[0], imdims[1], dx, dy); +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexa64 b/SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexa64 new file mode 100755 index 0000000..272563f Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexa64 differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexglx b/SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexglx new file mode 100755 index 0000000..9fc5d10 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcSobelMex.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcSobelPyrMex.cc b/SD-VBS/common/toolbox/lagrcv/calcSobelPyrMex.cc new file mode 100755 index 0000000..314c835 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcSobelPyrMex.cc @@ -0,0 +1,44 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [dxPye dyPyr] = + // calcGradientPyrMex(imagePyr) + + const int *cellDims = mxGetDimensions(prhs[0]); + double *image; + const mxArray* imgArray; + mxArray *dxArray, *dyArray; + double *dx, *dy; + const int *imdims; + + plhs[0] = mxCreateCellArray(2, cellDims); + plhs[1] = mxCreateCellArray(2, cellDims); + + for(int i=0; i +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [dxPye dyPyr] = + // calcGradientPyrMex(imagePyr) + + double *image, *retImg; + int newSizeY, newSizeX; + const int *imdims; + + image=(double*)mxGetPr(prhs[0]); + imdims = mxGetDimensions(prhs[0]); + + newSizeY=imdims[0]/2; + newSizeX=imdims[1]/2; + plhs[0] = mxCreateNumericMatrix(newSizeY, newSizeX, mxDOUBLE_CLASS, mxREAL); + retImg=(double*)mxGetPr(plhs[0]); + + calcSubSampleAvg(image, imdims[0], imdims[1], retImg, newSizeY, newSizeX); +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexa64 b/SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexa64 new file mode 100755 index 0000000..6cf4f0e Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexa64 differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexglx b/SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexglx new file mode 100755 index 0000000..f8acfde Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcSubsampleAvgMex.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcTextureMex.cc b/SD-VBS/common/toolbox/lagrcv/calcTextureMex.cc new file mode 100755 index 0000000..f91d184 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcTextureMex.cc @@ -0,0 +1,53 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ lambda tr det c_xx c_xy c_yy] = + // goodFeaturesToTrack(image, winSize) + // image must be single-channel, 8-bit + + double *image; + int winSize = (int)mxGetScalar(prhs[1]); + double *lambda, *tr, *det, *c_xx, *c_xy, *c_yy; + const int *imdims; + //double dx[360000];//[MAX_IMAGE_SIZE_1D]; + //double dy[360000];//[MAX_IMAGE_SIZE_1D]; + double *dx, *dy; + + image = (double*)mxGetPr(prhs[0]); + imdims = mxGetDimensions(prhs[0]); + dx=(double*)malloc(sizeof(double)*imdims[0]*imdims[1]); + dy=(double*)malloc(sizeof(double)*imdims[0]*imdims[1]); + + plhs[0] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[2] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[3] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[4] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[5] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + + lambda = (double*)mxGetPr(plhs[0]); + tr = (double*)mxGetPr(plhs[1]); + det = (double*)mxGetPr(plhs[2]); + c_xx = (double*)mxGetPr(plhs[3]); + c_xy = (double*)mxGetPr(plhs[4]); + c_yy = (double*)mxGetPr(plhs[5]); + + calcSobel(image, imdims[0], imdims[1], dx, dy); + calcGoodFeature(dx, dy, imdims[0], imdims[1], winSize, + lambda, tr, det, c_xx, c_xy, c_yy); + free(dx); + free(dy); +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexa64 b/SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexa64 new file mode 100755 index 0000000..3c023ea Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexa64 differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexglx b/SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexglx new file mode 100755 index 0000000..f8e0c99 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcTextureMex.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.cc b/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.cc new file mode 100755 index 0000000..3c332ca --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.cc @@ -0,0 +1,54 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ lambda tr det c_xx c_xy c_yy] = + // calcTexturePyrMex(dxPyr, dyPyr, winSize, level) + // image is assumed to be double + // the lowest level is 1 + + const int *cellDims = mxGetDimensions(prhs[0]); + int level=0; + char winSize = (char )mxGetScalar(prhs[2]); + double *dx, *dy; + const mxArray* dxArray, * dyArray; + const int *imdims; + double *tr, *det, *lambda, *c_xx, *c_xy, *c_yy; + + if (nrhs > 3) level = (int)mxGetScalar(prhs[3])-1; + + dxArray= mxGetCell(prhs[0],level); + dyArray= mxGetCell(prhs[1],level); + dx=mxGetPr(dxArray); + dy=mxGetPr(dyArray); + imdims = mxGetDimensions(dxArray); + + plhs[0] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[2] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[3] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[4] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + plhs[5] = mxCreateNumericMatrix(imdims[0], imdims[1], mxDOUBLE_CLASS, mxREAL); + + lambda = (double*)mxGetPr(plhs[0]); + tr = (double*)mxGetPr(plhs[1]); + det = (double*)mxGetPr(plhs[2]); + c_xx = (double*)mxGetPr(plhs[3]); + c_xy = (double*)mxGetPr(plhs[4]); + c_yy = (double*)mxGetPr(plhs[5]); + + calcGoodFeature(dx, dy, imdims[0], imdims[1], winSize, lambda, tr, det, c_xx, c_xy, c_yy); + +} diff --git a/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexa64 b/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexa64 new file mode 100755 index 0000000..ebede53 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexa64 differ diff --git a/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexglx b/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexglx new file mode 100755 index 0000000..697ceec Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/calcTexturePyrMex.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/dummyMex.cc b/SD-VBS/common/toolbox/lagrcv/dummyMex.cc new file mode 100755 index 0000000..0799182 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/dummyMex.cc @@ -0,0 +1,58 @@ + +/* compile with +rm liblagrcv.a +gcc -c lagrcv.cpp +ar rc liblagrcv.a lagrcv.o +ranlib liblagrcv.a +mex7 calcTextureMex.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [newFeaturePnt validFlag ] = + // calcOptFlowPyrLKMex(Ipyr, IdxPyr, IdyPyr, Jpyr, featurePnt, winSize, c_level, c_det, c_xx, c_xy, c_yy) + // Ipyr, IdxPyr, IdyPyr, Jpyr: levelx1 size cell. + // featurePnt 2xn int + // winSize c_level int + // c_xx c_xy c_yy: image size double* + // image must be single-channel, 8-bit + + + double* fPntDouble; + const int *nFeatures; + char* valid; + int *fPnt, *newFPnt; + + fPntDouble=(double*)mxGetPr(prhs[0]); + nFeatures=mxGetDimensions(prhs[0]); + fPnt=(int*)malloc(sizeof(int)*nFeatures[0]*nFeatures[1]); + + printf("nFeatures %d, %d", nFeatures[0], nFeatures[1]); + //plhs[0] = mxCreateNumericMatrix(nFeatures[0], nFeatures[1], mxINT32_CLASS, mxREAL); + plhs[0] = mxCreateNumericArray(2,nFeatures,mxINT32_CLASS, mxREAL); + //plhs[1] = mxCreateNumericMatrix(1, nFeatures[1], mxINT32_CLASS, mxREAL); + plhs[1] = mxCreateNumericArray(1,nFeatures+1,mxUINT8_CLASS, mxREAL); + + newFPnt = (int*)mxGetPr(plhs[0]); + valid = (char*)mxGetPr(plhs[1]); + + //idx convert from matlab to c + for(int i=0; i +#include + +//#define MAX_CORNERS 500 +#define WIN_SIZE 5 + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ features ] = + // findCornerSubPix(image, features) + // image must be single-channel, 8-bit + + char *image_pr = (char*)mxGetPr(prhs[0]); + const int *imdims = mxGetDimensions(prhs[0]); + IplImage *image = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + image->imageData = image_pr; + + const int *feature_dims = mxGetDimensions(prhs[1]); + int nfeatures = feature_dims[1]; + plhs[0] = mxCreateNumericMatrix(2, nfeatures, mxSINGLE_CLASS, mxREAL); + CvPoint2D32f *newfeatures = (CvPoint2D32f*)mxGetPr(plhs[0]); + CvPoint2D32f *oldfeatures = (CvPoint2D32f*)mxGetPr(prhs[1]); + // plhs[0] = mxDuplicateArray(prhs[1]); + // CvPoint2D32f *newfeatures = (CvPoint2D32f*)mxGetPr(plhs[0]); + memcpy(newfeatures, oldfeatures, sizeof(float)*2*nfeatures); + + cvFindCornerSubPix(image, newfeatures, nfeatures, + cvSize(WIN_SIZE,WIN_SIZE), + cvSize(-1,-1), + cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03)); +} diff --git a/SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.cc b/SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.cc new file mode 100755 index 0000000..376a096 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.cc @@ -0,0 +1,62 @@ + +/* compile with + mex goodFeaturesToTrack.cc -I/usr/local/opencv/include -L/usr/local/opencv/lib -lcxcore -lcv +*/ + +#include "mex.h" +#include "opencv/cv.h" +#include "opencv/highgui.h" +#include +#include + +#define MAX_CORNERS 500 + +IplImage *eigtemp = NULL, *temp2 = NULL; + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ features numvalid ] = + // goodFeaturesToTrack(image, quality, mindist, mask) + // image must be single-channel, 8-bit + // quality = minimum acceptable ratio of eigenvalues + // mindist = minimum distance between corners + // mask (optional) = bitmap mask "region of interest" (MUST BE uint8 TYPE!) + + char *image_pr = (char*)mxGetPr(prhs[0]); + // int imdims[] = { (int)d_imdims[0], (int)d_imdims[1] }; + const int *imdims = mxGetDimensions(prhs[0]); + double quality = mxGetScalar(prhs[1]); + double mindist = mxGetScalar(prhs[2]); + bool use_roi = nrhs > 3; + + plhs[0] = mxCreateNumericMatrix(2, MAX_CORNERS, mxSINGLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericMatrix(1, 1, mxINT32_CLASS, mxREAL); + + CvPoint2D32f *corner_coords = (CvPoint2D32f*)mxGetPr(plhs[0]); + int *corner_count = (int*)mxGetPr(plhs[1]); + *corner_count = MAX_CORNERS; + + if (eigtemp == NULL) { + eigtemp = cvCreateImage(cvSize(imdims[0],imdims[1]), IPL_DEPTH_32F, 1); + temp2 = cvCreateImage(cvSize(imdims[0],imdims[1]), IPL_DEPTH_32F, 1); + } + + IplImage *image = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + image->imageData = image_pr; + + IplImage *roimask = NULL; + if (use_roi) { + roimask = cvCreateImage(cvSize(imdims[0],imdims[1]), IPL_DEPTH_8U, 1); + roimask->imageData = (char*)mxGetPr(prhs[3]); + } + + cvGoodFeaturesToTrack(image, + eigtemp, temp2, + corner_coords, + corner_count, + quality, + mindist, + roimask + ); +} diff --git a/SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.mexglx b/SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.mexglx new file mode 100755 index 0000000..0fe9080 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/goodFeaturesToTrack.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/lagrcv.cpp b/SD-VBS/common/toolbox/lagrcv/lagrcv.cpp new file mode 100755 index 0000000..4f24b5d --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/lagrcv.cpp @@ -0,0 +1,840 @@ +#include "lagrcv.h" +/* +void calcImagePyr(char *src, int sizeY, int sizeX, int level, char *pyr){ + pyr=(char*) malloc(sizeof(char)*(int)sizeY*sizeX*4/3); + int i, startPnt, nX, nY; + + memcpy(pyr,src, sizeY*sizeX); + nY=sizeY; + nX=sizeX; + for(i=0; i> 1; + nX = (nX + 1) >> 1; + calcSubsample(pyr, sizeY, sizeX, pyr+sizeY*sizeX) + } + free(pyr); +} +*/ + +void calcSubSampleAvg(double *src, int sizeY, int sizeX, double *dest, int destSizeY, int destSizeX){ + int i, j, idx, + destI, destJ, idxDest; + for(i=0, destI=0; destI0)?sum[idx-1]:0) + + ((i>0)?sum[idx-sizeY]:0) + - ((i>0&&j>0)?sum[idx-sizeY-1]:0); + } + } +*/ + idxDelta[0]=halfSize*sizeY+halfSize; + idxDelta[1]=-halfSize*sizeY+halfSize; + idxDelta[2]=halfSize*sizeY-halfSize; + idxDelta[3]=-halfSize*sizeY-halfSize; + for(i=halfSize; i=0; level--){ + x+=x; y+=y; dX+=dX; dY+=dY; + imgSize[0]=imgDims[level*2+0]; //y,x + imgSize[1]=imgDims[level*2+1]; //y,x + + c_xx=c_xy=c_yy=0; + //when feature goes out to the boundary. + if((x-winSize-1)<0 || (y-winSize-1)<0 + || (y+winSize+1+1)>=imgSize[0] || (x+winSize+1+1)>=imgSize[1]){ + //winSize+1due to interpolation + //error or skip the level?? + valid[i]=0; + break; + } + + getInterpolatePatch(iP[level], imgSize, x, y, winSize+1, iPatch); //to calculate iDx, iDy + calcSobel(iPatch, (winSize+1)*2, (winSize+1)*2, iDxPatch, iDyPatch); + for(k=0; k <(winSize*2); k++ ){ + memcpy( iPatch + k*winSize*2, iPatch + (k+1)*(winSize+1)*2 + 1, winSize*2 ); + memcpy( iDxPatch + k*winSize*2, iDxPatch + (k+1)*(winSize+1)*2 + 1, winSize*2 ); + memcpy( iDyPatch + k*winSize*2, iDyPatch + (k+1)*(winSize+1)*2 + 1, winSize*2 ); + } + + for(idx=0; idx=imgSize[0] || (x+dX+winSize+1)>=imgSize[1]){ + //winSize+1due to interpolation + //error or skip the level?? + valid[i]=0; + break; + } + getInterpolatePatch(jP[level], imgSize, x+dX, y+dY, winSize, jPatch); + eX=eY=0; + for(idx=0;idx0 && (mX + prevMX) < 0.01 && (mX+prevMX) > -0.01 + && (mY + prevMY) < 0.01 && (mY+prevMY) > -0.01) + { + dX -= mX*0.5f; + dY -= mY*0.5f; + break; + } + prevMX=mX; + prevMY=mY; + } + if(k==max_iter){ + valid[i]=0; + } + } + newFPnt[i*2+0]=fPnt[i*2+0]+dX; + newFPnt[i*2+1]=fPnt[i*2+1]+dY; +/* + if(valid[i] + || (x+dX-winSize)<0 || (y+dY-winSize)<0 + || (y+dY+winSize+1)>=imgSize[0] || (x+dX+winSize+1)>=imgSize[1]){ + newFPnt[i*2+0]=fPnt[i*2+0]+dX; + newFPnt[i*2+1]=fPnt[i*2+1]+dY; + getInterpolatePatch(jP[0], imgSize, newFPnt[i*2+0], newFPnt[i*2+0], winSize, jPatch); + dIt=0; + for(idx=0;idxwinSizeSq*50000){ + valid[i]=0; + } + }else{ + newFPnt[i*2+0]=0; + newFPnt[i*2+1]=0; + } +*/ + } + free(iPatch); + free(jPatch); + free(iDxPatch); + free(iDyPatch); +} +void calcPyrLKTrackWInit(double** iP, double** iDxP, double** iDyP, double** jP, const int* imgDims, int pLevel, + double* fPnt, int nFeatures, int winSize, + double* newFPnt, double* initFPnt, + char* valid, double accuracy_th, int max_iter){ + + double x, y, eX, eY, dX, dY, mX, mY, prevMX, prevMY, c_xx, c_xy, c_yy, c_det, dIt; + double* iPatch, *jPatch, *iDxPatch, *iDyPatch; + int level, winSizeSq; + int i, k, idx; + int imgSize[2]; + + static const double rate[]={1, 0.5, 0.25, 0.125, 0.0625, 0.03125}; + winSizeSq=4*winSize*winSize; + + iPatch=(double*) malloc(sizeof(double)*winSizeSq); + jPatch=(double*) malloc(sizeof(double)*winSizeSq); + iDxPatch=(double*) malloc(sizeof(double)*winSizeSq); + iDyPatch=(double*) malloc(sizeof(double)*winSizeSq); + + for(i=0; i=0; level--){ + x+=x; y+=y; dX+=dX; dY+=dY; + imgSize[0]=imgDims[level*2+0]; //y,x + imgSize[1]=imgDims[level*2+1]; //y,x + + c_xx=c_xy=c_yy=0; + //when feature goes out to the boundary. + if((x-winSize)<0 || (y-winSize)<0 + || (y+winSize+1)>=imgSize[0] || (x+winSize+1)>=imgSize[1]){ + //winSize+1due to interpolation + //error or skip the level?? + valid[i]=0; + break; + } + + getInterpolatePatch(iP[level], imgSize, x, y, winSize, iPatch); + getInterpolatePatch(iDxP[level], imgSize, x, y, winSize, iDxPatch); + getInterpolatePatch(iDyP[level], imgSize, x, y, winSize, iDyPatch); + + for(idx=0; idx=imgSize[0] || (x+dX+winSize+1)>=imgSize[1]){ + //winSize+1due to interpolation + //error or skip the level?? + valid[i]=0; + break; + } + getInterpolatePatch(jP[level], imgSize, x+dX, y+dY, winSize, jPatch); + eX=eY=0; + for(idx=0;idx0 && (mX + prevMX) < 0.01 && (mX+prevMX) > -0.01 + && (mY + prevMY) < 0.01 && (mY+prevMY) > -0.01) + { + dX -= mX*0.5f; + dY -= mY*0.5f; + break; + } + prevMX=mX; + prevMY=mY; + } + if(k==max_iter){ + valid[i]=0; + } + } + newFPnt[i*2+0]=fPnt[i*2+0]+dX; + newFPnt[i*2+1]=fPnt[i*2+1]+dY; +/* + if(valid[i] + || (x+dX-winSize)<0 || (y+dY-winSize)<0 + || (y+dY+winSize+1)>=imgSize[0] || (x+dX+winSize+1)>=imgSize[1]){ + newFPnt[i*2+0]=fPnt[i*2+0]+dX; + newFPnt[i*2+1]=fPnt[i*2+1]+dY; + getInterpolatePatch(jP[0], imgSize, newFPnt[i*2+0], newFPnt[i*2+0], winSize, jPatch); + dIt=0; + for(idx=0;idxwinSizeSq*50000){ + valid[i]=0; + } + }else{ + newFPnt[i*2+0]=0; + newFPnt[i*2+1]=0; + } +*/ + } + free(iPatch); + free(jPatch); + free(iDxPatch); + free(iDyPatch); +} +void calcPyrLKTrack(double** iP, double** iDxP, double** iDyP, double** jP, const int* imgDims, int pLevel, + double* fPnt, int nFeatures, int winSize, + double* newFPnt, char* valid, double accuracy_th, int max_iter){ + + double x, y, eX, eY, dX, dY, mX, mY, c_xx, c_xy, c_yy, c_det, dIt; + double* iPatch, *jPatch, *iDxPatch, *iDyPatch; + int level, winSizeSq; + int i, k, idx; + int imgSize[2]; + + static const double rate[]={1, 0.5, 0.25, 0.125, 0.0625, 0.03125}; + winSizeSq=4*winSize*winSize; + + iPatch=(double*) malloc(sizeof(double)*winSizeSq); + jPatch=(double*) malloc(sizeof(double)*winSizeSq); + iDxPatch=(double*) malloc(sizeof(double)*winSizeSq); + iDyPatch=(double*) malloc(sizeof(double)*winSizeSq); + + for(i=0; i=0; level--){ + x+=x; y+=y; dX+=dX; dY+=dY; + imgSize[0]=imgDims[level*2+0]; //y,x + imgSize[1]=imgDims[level*2+1]; //y,x + + c_xx=c_xy=c_yy=0; + //when feature goes out to the boundary. + if((x-winSize)<0 || (y-winSize)<0 + || (y+winSize+1)>=imgSize[0] || (x+winSize+1)>=imgSize[1]){ + //winSize+1due to interpolation + //error or skip the level?? + valid[i]=0; + break; + } + + getInterpolatePatch(iP[level], imgSize, x, y, winSize, iPatch); + getInterpolatePatch(iDxP[level], imgSize, x, y, winSize, iDxPatch); + getInterpolatePatch(iDyP[level], imgSize, x, y, winSize, iDyPatch); + + for(idx=0; idx=imgSize[0] || (x+dX+winSize+1)>=imgSize[1]){ + //winSize+1due to interpolation + //error or skip the level?? + valid[i]=0; + break; + } + getInterpolatePatch(jP[level], imgSize, x+dX, y+dY, winSize, jPatch); + eX=eY=0; + for(idx=0;idx=imgSize[0] || (x+dX+winSize+1)>=imgSize[1]){ + newFPnt[i*2+0]=fPnt[i*2+0]+dX; + newFPnt[i*2+1]=fPnt[i*2+1]+dY; + getInterpolatePatch(jP[0], imgSize, newFPnt[i*2+0], newFPnt[i*2+0], winSize, jPatch); + dIt=0; + for(idx=0;idxwinSizeSq*50000){ + valid[i]=0; + } + }else{ + newFPnt[i*2+0]=0; + newFPnt[i*2+1]=0; + } +*/ + } + free(iPatch); + free(jPatch); + free(iDxPatch); + free(iDyPatch); +} +void getPatch(double* srcImg, const int* srcDims, double centerX, double centerY, int winSize, double** dstImg){ + int srcIdxX, srcIdxY, dstIdxX; + srcIdxY=(int)centerY-winSize; + for(int i=-winSize; i=imdims[0] || (x+winSize)>=imdims[1]){ + //error or skip the level?? + valid[i]=0; + continue; + } + + getPatch(imgI, imdims, x, y, winSize, iPatch); + getPatch(iDx, imdims, x, y, winSize, iDxPatch); + getPatch(iDy, imdims, x, y, winSize, iDyPatch); + + idx=(int)x*imdims[0]+(int)y; + c_det=c_xx[i]*c_yy[i]-c_xy[i]*c_xy[i]; + if(c_det/(c_xx[i]+c_yy[i]+0.00000001)=imdims[0] || (x+dX+winSize+1)>=imdims[1]){ + //winSize+1due to interpolation + //error or skip the level?? + valid[i]=0; + break; + } + getInterpolatePatch(imgJ, imgSize, x+dX, y+dY, winSize, jPatch); + eX=eY=0; + for(idxCol=0;idxCol<2*winSize;idxCol++){ + for(idxRow=0;idxRow<2*winSize;idxRow++){ + dIt=iPatch[idxCol][idxRow]-jPatch[idxCol*winSize*2+idxRow]; + eX+=dIt*iDxPatch[idxCol][idxRow]; + eY+=dIt*iDyPatch[idxCol][idxRow]; + } + } + mX=c_det*(eX*c_yy[i]-eY*c_xy[i]); + mY=c_det*(-eX*c_xy[i]+eY*c_xx[i]); + dX+=mX; + dY+=mY; + if((mX*mX+mY*mY) +#include +#include +#include +#include +#include +#include + +#define GOOD_FEATURE_LAMBDA_TH 10 +#define LK_ACCURACY_TH 0.03 +#define LK_MAX_ITER 20 +#define LK_MAX_WIN 25 +#define MAX_LEVEL 10 +#define MAX_IMAGE_SIZE_1D 1000000 + +void calcSubSampleAvg(double *src, int sizeY, int sizeX, double *dest, int destSizeY, int destSizeX); +void calcImgBlur(double *src, int sizeY, int sizeX, double *dest); +void calcImgResize(double *src, int sizeY, int sizeX, double *dest, int dstSizeY, int dstSizeX); +void calcGradient(double *src, int sizeY, int sizeX, double *dX, double *dY); +void calcGradient(char *src, int sizeY, int sizeX, char *dX, char *dY); +void calcSobel(double *src, int sizeX, int sizeY, double *dx, double *dy); +void calcGoodFeature(double *dX, double *dY, int sizeY, int sizeX, int winSize, double* lambda, double* tr, double* det, + double* c_xx, double* c_xy, double* c_yy); +void calcGoodFeature(char *dX, char *dY, int sizeY, int sizeX, int winSize, float* lambda, float* tr, float* det); +void calcMinEigenValue(char *dX, char *dY, int sizeY, int sizeX, float* lambda); +void calcAreaSum(int *src, int sizeY, int sizeX, int sizeSum, int *ret); +void calcAreaSum(double *src, int sizeY, int sizeX, int sizeSum, double *ret); +void calcPyrLKTrack(double** iP, double** iDxP, double** iDyP, double** jP, const int* imgDims, int pLevel, + double* fPnt, int nFeatures, int winSize, + double* newFPnt, char* valid); +void calcPyrLKTrack(double** iP, double** iDxP, double** iDyP, double** jP, const int* imgDims, int pLevel, + double* fPnt, int nFeatures, int winSize, + double* newFPnt, char* valid, + double accuracy_th, int iter); +void calcLKTrack(double* imgI, double* iDx, double* iDy, double* imgJ, const int* imdims, + double* c_xx, double* c_xy, double* c_yy, + double* fPnt, double* initPnt, int nFeatures, int winSize, + double* newFPnt, char* valid, + double accuracy_th, int max_iter); +void calcLKTrack(double* imgI, double* iDx, double* iDy, double* imgJ, const int* imdims, + double* c_xx, double* c_xy, double* c_yy, + double* fPnt, double* initPnt, int nFeatures, int winSize, + double* newFPnt, char* valid); +void calcPyrLKTrackWInit(double** iP, double** iDxP, double** iDyP, double** jP, const int* imgDims, int pLevel, + double* fPnt, int nFeatures, int winSize, + double* newFPnt, double* initFPnt, + char* valid, double accuracy_th, int max_iter); +void calcPyrLKTrackWInit(double** iP, double** jP, const int* imgDims, int pLevel, + double* fPnt, int nFeatures, int winSize, + double* newFPnt, double* initFPnt, + char* valid, double accuracy_th, int max_iter); diff --git a/SD-VBS/common/toolbox/lagrcv/liblagrcv.a b/SD-VBS/common/toolbox/lagrcv/liblagrcv.a new file mode 100755 index 0000000..a7a39ae Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/liblagrcv.a differ diff --git a/SD-VBS/common/toolbox/lagrcv/lk_flow.cc b/SD-VBS/common/toolbox/lagrcv/lk_flow.cc new file mode 100755 index 0000000..c083048 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/lk_flow.cc @@ -0,0 +1,76 @@ + +/* compile with + lk_flow.cc -I/usr/local/opencv/include -L/usr/local/opencv/lib -lcxcore -lcv +*/ + +#include "mex.h" +#include "opencv/cv.h" +#include "opencv/highgui.h" +#include +#include + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ velx vely ] = lk_flow(image1,image2,winsize) + // images must be single-channel, 8-bit + + const int *backwards_imdims = mxGetDimensions(prhs[0]); + int imdims[2] = { backwards_imdims[1], backwards_imdims[0] }; + int winsize = (int)mxGetScalar(prhs[2]); + plhs[0] = mxCreateNumericArray(2, imdims, mxSINGLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericArray(2, imdims, mxSINGLE_CLASS, mxREAL); + + printf("imsize %d %d\n", imdims[0], imdims[1]); + + IplImage *im1 = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + IplImage *im2 = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_8U, 1); + IplImage *flow_x = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_32F, 1); + IplImage *flow_y = + cvCreateImageHeader(cvSize(imdims[0], imdims[1]), IPL_DEPTH_32F, 1); + + im1->imageData = (char*)mxGetPr(prhs[0]); + im2->imageData = (char*)mxGetPr(prhs[1]); + flow_x->imageData = (char*)mxGetPr(plhs[0]); + flow_y->imageData = (char*)mxGetPr(plhs[1]); + + cvCalcOpticalFlowLK(im1, im2, cvSize(winsize,winsize), flow_x, flow_y); + + for (int row = 0; row < imdims[0]; row += 10) { + for (int col = 0; col < imdims[1]; col += 10) { + cvLine(im1, cvPoint(col,row), + cvPoint(int(col + flow_x->imageData[imdims[1] * row + col]), + int(row + flow_y->imageData[imdims[1] * row + col])), + CV_RGB(1,0,0), + 1, + CV_AA, + 0); + + } + } + + cvDestroyAllWindows(); + cvNamedWindow("imfoo",CV_WINDOW_AUTOSIZE); + // IplImage *myim = cvLoadImage("lena.jpg", -1); + cvShowImage("imfoo",im1); + // cvShowImage("imfoo",myim); + cvWaitKey(0); + + // cvCircle(flow_x, cvPoint( 40, 20), 15, CV_RGB(10,100,100), 5); + + /* + FILE* filefx = fopen("fx.pgm", "w"); + FILE* filefy = fopen("fy.pgm", "w"); + fprintf(filefx, "P5\n%d %d\n255\n", imdims[1], imdims[0]); + fprintf(filefy, "P5\n%d %d\n255\n", imdims[1], imdims[0]); + float *ptr = (float*)flow_x->imageData; + for (int i = 0; i < imdims[1] * imdims[0]; i++) { + // if (fabs(++ptr*) > 0.1) + if (fabs(*ptr++) > 0.1) fprintf(filefx, "1"); + else fprintf(filefx, "0"); + } + fclose(filefx); + fclose(filefy); + */ +} diff --git a/SD-VBS/common/toolbox/lagrcv/lk_flow.mexglx b/SD-VBS/common/toolbox/lagrcv/lk_flow.mexglx new file mode 100755 index 0000000..d6c1136 Binary files /dev/null and b/SD-VBS/common/toolbox/lagrcv/lk_flow.mexglx differ diff --git a/SD-VBS/common/toolbox/lagrcv/test.cc b/SD-VBS/common/toolbox/lagrcv/test.cc new file mode 100755 index 0000000..d18144c --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/test.cc @@ -0,0 +1,34 @@ + +/* compile with + mex goodFeaturesToTrack.cc -I/usr/local/opencv/include -L/usr/local/opencv/lib -lcxcore -lcv +mex test.cc -L/home/ikkjin/LagrMatlab/opencv/matlab -llagrcv -I/home/ikkjin/LagrMatlab/opencv/matlab/ +*/ + +#include "mex.h" +#include "lagrcv.h" +#include +#include + +#define MAX_CORNERS 500 +#define MAX_SIZE 700 + +// TODO: add number of corners parameter +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + // usage: [ features numvalid ] = + // goodFeaturesToTrack(image, quality, mindist, mask) + // image must be single-channel, 8-bit + // quality = minimum acceptable ratio of eigenvalues + // mindist = minimum distance between corners + // mask (optional) = bitmap mask "region of interest" (MUST BE uint8 TYPE!) + + char *image = (char*)mxGetPr(prhs[0]); + char *sum; + // int imdims[] = { (int)d_imdims[0], (int)d_imdims[1] }; + const int *imdims = mxGetDimensions(prhs[0]); + + plhs[0] = mxCreateNumericMatrix(imdims[0], imdims[1], mxINT8_CLASS, mxREAL); + + sum = (char*)mxGetPr(plhs[0]); + + calcAreaSum(image, imdims[0], imdims[1], 8, sum); +} diff --git a/SD-VBS/common/toolbox/lagrcv/test/getPyramid.m b/SD-VBS/common/toolbox/lagrcv/test/getPyramid.m new file mode 100755 index 0000000..ecae078 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/test/getPyramid.m @@ -0,0 +1,10 @@ +function pyr=getPyramid(img, level) +kernel=[1/16 1/4 3/8 1/4 1/16]; +pyr=cell(level,1); +pyr{1}=double(img); +for i=2:level +% imgBlur=conv2(pyr{i-1}, kernel, 'same'); +% imgBlur=conv2(imgBlur, kernel, 'same'); +% pyr{i}=imgBlur(1:2:end, 1:2:end); + pyr{i}=calcResizedImgMex(pyr{i-1}); +end diff --git a/SD-VBS/common/toolbox/lagrcv/test/img0.ppm b/SD-VBS/common/toolbox/lagrcv/test/img0.ppm new file mode 100755 index 0000000..816e0ad --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/test/img0.ppm @@ -0,0 +1,3137 @@ +P6 +512 384 +255 +          +     +     + + + +  +    +   # + +       +          +        # "08%9+!5.(;(07.)2.#3*#+'&)!##).!%.")#%!!"$% "#   ! %)$32+):/, )$"%28)+&!$"&   ! %#('(%$!/!+&"1/34-1"6 5 *" !   +    !%       +   +  +'491F,<6($ "!%'!"*-#(   +  "2* # +#?64&DC$1$#./+2.)$ 0-"-A*&2$",/.?>::4@,5$/7A58T*37,%(("/72>,1-20>=):.8!2  % 5%;&6    +   +  " +  #'O$6L'CC:KTLri@Kf5QV(KHE<#E7,*#(+-FH2FB&?:0?C7CM8@<0JE*?F.99(-',?/6;?/@J2QY%W[!7O+3?8:H=?J7GKPdCP]IZmIWp`I]YlvyyRx@j7a{8YgAA@[AKMTSHVZM[oXVbQJcL]lL]r^p˱̗dq[Z]SIPZCOSKLULJRMQTPWSpn~dYcQHWdA\mQceGHE31257=CNENI8>A;1;U3Bh9Mp,J&G0<`4:J       +      +  + + +  +    +  +     +  $    +  "                 #"  .7 8, ,,)+)*+'%*#"&!#" &#&"!"'& %#"  !  # %).,*&-)#5).577 2?!5! ##"! *#$(!+))+-)&,#064'**$%!! " "!    $ $ +%   + + + +   +    +  ( "!#++"-0*- #'"-0#+*"# "  #)+*'* +* # !$*6*4;2-73*40:8+9" !! !+$22&)!$*(+1(2+0-(%17(9J*,A%"%"'#&/#3-1>(,(3:(,+1&3*%"& +  +   + * +  +  ,* +)%    ;(/E?Z*(",       % &  60 +-A d;!       + &1$      $  !!!$",'!"!"! &&(!%+ ,/%" %#);#.N*,BOZFnx,~c-L?0/8"QR(@Q9PdRY\rOQfAQ]+L])A_&?H!1;2;L84G28G1MJA3;?=A8KNGLRGJ`@CN==A6*3-11*/47:A7ZPD>7C@6J9CQ7CQ:F[?YW;LX>Wk:av@hzDhgZ`or^oaa_RpAgBLr@GZ0AN?CPL8NVIbWCSU.GZRZ__mqɇwuyxlVUN_RKZXLU]JTYHTOCULHg]cɬzXtwXdb`ZP_WQM^XaZQJA892246C?FGJH?@>449<>C'??#19'#,,+-1-,('!''%"!#'(&*%'!% ( !16!/6!,%.&-$'$ !6 5,I60@/3(7*."%*     +' # !"'  +(**5;4 +E& " " *1-G0I.94/  +   & /. ?? +}@ ]9T)>/#="  +      ##:5-1        "!"%!&##"  #"!#')&"''*#-3%D7-`GGzm_`~F{y-ig1[c<;F?1=^Gcg@R\A@U=7O<.=D.>1->40;A8?755D8B>0;45;387=247M@6CBC[_FWU-I@/<7F:GGCOS;R\ASkD[nJpyEoQs_wbhgZecf|Ng}54rAKaBQ7@I;5@7"4.*7).6&>:,B@.6=42;84:-=>23@++/2);A1DAU^Ftr@DY/=H+2A*4@&9B/48-GE9GM?S^?[\BSWB8L6?M=JY9M]:OT64>:6CC=FEN_IG`MSj\Ucc^hhk\QlQ`vZ]{NdM{XlBUk:JH7;B8?M3AH/29,-15*62)32         +       +    +     -**(3-*6(1&, ($" +       +   +     #% )($%(%$!# &05)G*'2/)+! + "    ""       %$)'-."&$#'%(./  !%# '!),(1!$&2%&*'  #"'$    + + +  +  +     "   +!0&2$ /)"P D"! + +  "  +49 9'$#'!#&!#-2!  '#(&%&#%!  %$"& #,/ 035'#0+%--($* $'#1"!(+$0!*!%.%5$>&D*)33$$&$"'!0&G92J  <2 GIFJ 4Z:L4O.O/    %##% $&+() 3A9"lk$>o-<%697>WNhR\&UZ/5RF;b9'S%$(    +   &,!77 +9$"0' (4 &  !!"%"'$""+&0% ! !&#*C1Jd=ac\hV^hbD_KDMIMN?A?1SG7DLC1AA+A0EP$GG"88&((,#3/0A45B0;>->B+FE*4@*4;('*4.*<7EON]QCI=<@20:,;A09<4?:1CBAGRAGPO?[SH`FQTNAEAJKDRN@KS?H]?\YRyg`}UuYyLZcMJk[LfoLk\I]BUY3UW?0%-7)(/#   !#    $ "'$%!' $(*#+*(%2"!1#       + +  + +  +  +  +  +   + !#$ !+'*'" +  '"!# ! '3",. &' &%$.*/-0*:3D:605/=/((!!#& " #'.$%/9#"8!%("3" #)*"+0!<#6"9/''+#*&.<%**'$,2F:8)'$<5!&>3'1'3-5$/31#E,!C/;()!    + &,GG3526,<.=#!  ! +%1J%CC#! &7#-M')@7$5="  + +   %,1%75A0"-#!-2@.X2L;.;!8/    !"!&!'%.''0+)*#( %)%#8')D*8QCQbOYEDQ/0:-+3#43!D=(OK:?CE-?4374334;335418=4FB=HO>:L>.:G3039/;;4D=8AGD7?DJJCMM/7?JM>faP]hwUs\x?Y2Q7W=]cC\BD]:JWI_clNvCneFgBCGAKPg_MHrScr\jof_ހ~ezgudev[bzN^nK_idk֛l}yqkèvtgTTL=IfDMHX}STBMT:;P8:H;:F;9>513-42+61(  +        +   + !        +   + +", )0"1#-% !$,#!+-&%3%//5,!-,.)"+(!   +  +    "   +   + !$"!!%&#&"% *2:$ >0#2.-*).&  "%      + +    .%%'&/$!(" #&%"$"((    +  +  +  + + +  + % +!%, +!!'5 -& $        + #   ""$%!" #& ,%562"7@18=,49!+6+"/""!)!% &*")+#,("(&%,+&2 0,6)7"-1)"#1&$5!;'B !-!$("03-+E#)!-"1C2%F);9ODD945-,../&. 8#%# &" '* ) )3(!6"C-/>*/C(.64=B +*#"" % !-(06+0-/%/13##,% &%3!9!*!'26:)4I%+++&   9,5QKU 5":2 >+ <*?2%,hNG\>E?>!Tn=k$#S&'$       ! #!(!$#$$*+6-7-6:5S:T+B&M/C3 -*!674=HAEICAN=8P@0ED3FQ;JJ)1+((%+$(<6;I,KI"A:36#370NF9/B8.5--.&(,1&6)CM7JR93D1+;3-:,=@+=C,9@91;64B<3C>-87900D53LI:GD;9GG5C67;9<;H;e?mBTlQMbpUpnY^k\pJZh6wi7Yi7Fa40GE5@EFC=HSIUeMU[d?UwVeibWSA5K=HK9?G;A59E;,&+2(*&+'!!     ' #        +       + +    +  ("))0&!-)#!'"#!)$C$A6+98' + +    +  +      ! +& + 2 "%$* +           ! +..(%"    " *14$"#$'$1+5618@5 :2'&0++,$ ",2C6K9"01$1#)0$4/"1-,*$'(217=BQ= +0#!(')1"1"%0'. -23!9A853,0&% (.6!!7,4E!1L#8%8364;'4031, :(4,D+C1>7E/C-?BI1G$#(-"0<0?81-'$8#!"-2HC*72%/ "5"6!(<$8/".1( 3*$2,%%8B9K  0C!DIF'% ,$G)LON^N=C"8T>Gp4Rc 0A1,PG77          "$$").%1#,/!%14:*5H1Fj$\j'`e,G\';O#/A4-:=7:D9B5;K05A>82;69D:>B:B@I;><4U`1]r;YtL_P_AazOPtaUdaTk]^xVg{Ho<|z3hh/NK7B<7@D?=KN\lQcpEvrN^c`]hAOb/SW9.742H1HX5NaIMWJ6F?2E00:?(:[n^h}x~ͱ]zNN_XQmfSz`ZnNcoP\wSns¦k{o{pgfZU^=JMBCKLK\EGW>8E66=77@::C:AE<:K85E2/D3 +  + +   +     + +  +      +    + + +      '(.+*"1$+$"#*#*%%++!(6%+9/!:/0) + +  !  +    ! '",+%&' $47%;,1)"+%)/ &*  "   ) !%         +   + + + +     +   +       + +0*%2'#2! -%#".($5+=*+383,-,"*     +  + +        3 +*# (!    +  +  +  + $ + " +E(#!  +   !! )&   +***)%:5*,85%$+"+ &$%,!(F*6?.66+P9#EG$4=$5J3A574A,9 0%)+%0!25%2-2883K*6-/(2).""3),D)O120*(4'%.),-3+)4(.4!8@*D4)7#."#%!%"%&4'179B9V*+A(*A$J9&Lk?D8&*$<"9"J"6!2  %-50$ %%!(&"& "#0- E;384,:& 1 #*E9XD"c4/&!-*21H56U2JB:MG8.?):    + $ + +      "$.-'.+1&.;941B5+8<5@=0@33=+05=C$CA'A<;:C6EK1pb7_h;RU+<:+'"'(#'+-1.:JEN7KT1F\'NS+HP4_Y.IA+?=/MU@R_e_xiYLWB[J9RB1@B.<>1@C8EI3EJ6GIDAFQp@_`OdVX@T^Poi[s~HyZ_uFcb;BK=BJ>KQIfrRSʞOTjtTkwB\o8DE,+61:F1^:aW1MH.C;+?EgTdy{hu[[r}h苜ft:x8twGUxRUoGWgJ`k^`}zo⒙}{pUph\dNTZCSU9MSEIYHEL<;0752:5055   +     +  +      +  +  +   +     +  + $'$) #)',%/ ,#$     + +      & " ( 5A'73#30$)&)-%,!"#!   +  $ &$""        + + +          +    * )%#!'#!'%(" (+(.&.)40,4*2'%6.;10), '#   +  + +    ")( ' ** 0) -% ?/(65*0C(% +         ('N,V  +  +  + +& %#%%##**  &(%$-')%%'1,*&$/&1#34$8P)0S>0E3,6847-43(73661,+% !.'6&*7549E%15$/)&'1!#,2+$'/3<KH<49)3206@16)/(30#19/7(*- '$#/*#;$+&#$)'1(@5!8$#")2-#$$)$& #'!0)9#-E$*P4W7P', **1ff1L $O,I'7&)!&-:'4=3>1R`.jX-V#'<      8 > +1% .4%B$)1@7+ #&$&#*,G9]>$U>'DI70LZ@bPQ_8TS-en-8D$3.%0*'47-)3(,'-/(5-*/.-.,6-7B4U^8{\2dR2KQ8_kEgjXf]C@I,-6.7 #'"1-*:73/:=8A@-;K%:A$8G5MN:I729:/?D5H`DeNn~IOPRLIOQ9KE5VB37D6PR6@M7ZgH]eSdHgl5PSJgCOX5WZ1XZLTfXyl\wh]xkz{^ijꂞkj^zKgKl@dwBXkPShQPhSixt鮷ꧫtjO^\I]MP_BMY?KX@CS9CM;?D36B7081,43(02/3338./3- +       + +  +  +    +      +  +    ! %!%)+'      "&1:$>/ 00&2+(0%&( !!! #    &,,-   +  + +        +  + +   + +      +( "0#!!"''."%2%$-*&,#'#+#'   + +   + + + +    )$'%*#==?U'.F<4?@&G$       + )$):7#L#4"9G      ("+%%))&%   ! '( ! # .347882?6";?6;G?UD1TS XMGV ;P:U6R,K1G-::0663-/"71!5:"97/6:+,**3%;!,4.061 +&!(9)<'5*52A0<-Ag4+k70O9GG?99VEKCWQJhlNUm,NJ)7B'7P&PU(fT#NH#>9&56#42--8>:GKPiFmy@bi=9NQ:VYOdS?>8#)( (($($-2)1:)261<<3)12#5&2(.J1;;4:./928>?;CV/Aj1GM:IMAFIC@9BE83=9BMCOO?/T]9{v:ww@?x=FWEEWSJ^OSdO\sOW_OtkXz8`c=NOBRWEZSV}\VruHBXc{R[FNdIKbLKnJQxERi-Nf/Ub/KUGHbR@YIKc>KoBWoZWgWToMJr`ZmYlg}ak`[b_Hh]Lj`JjOOlG`oFktKat_אWnbE[SITNCTHFTEGTHFS;?O::E29@158+03./5004-&..).+   +   + +    +       +   +  + + +    !"          %*#"/6#F(F/)66)')&.'$%!  "#!'$ " % (       +  + + +         +  + +       #"($!               ?,F9D6(@*/&'*6&>'  # %  + +  & *1'"&"):!-*  +  + +  +2E+5<'# + +  "  *(&5.$""(!!" *$<)E.<%:?"MK.=bFFa>JWA>E7>G- 7%$)&3<#:H8R%GP"SSEGOE3B$38(&- "()!*,39A#DR!.>/9(-%!/&0'H8)/G/6M13K(&+A/7$#.-2'3*.<1(E!.   +  +   + +   %$! *,,<=7?5A]*(F&)8!4%%";'Q*J!)HJY-8z GOs{\|*-81)   +/;EP%S\&YQ,gbP["7D+5E>9EM(1J'&7!)-&;2(AANWBuQby9\d7JS5E8<_MCz8y7SW2-4)*:.4@1::T:F\5QDAJ:PEGhYS:E2+. ./,'#")01)36+6;0.8"0+"$ '(,++.90,/5,CC5OA3,0:%0>98FGC?7=@EA7=66E?BWY>jlJbKx@8<6EvC_]kxbtp_{S^|Q_u;ew9bl8^e9LfFPh2Ic/I_)Ol2Uo9Kj=O`?Ie@N_C?[DHfDJlXLvXWaoRpb\g]Ga\^snYtlDkNH\IWhNWjLSeVo|Yv]dxh~T[OCPDDRIEJB>FD8HB:M>5F63>1,7/,9.99,48--/,'-'%.(       +       + +  + +  +  +             +    # %)&&!&$"!("2$;*H)J7&19---*-"% $! "'" #!!"!      +  +   + +     +      +  +  +       + +        +    +   #!   >/56-5<+,(*$$ !$)'   6 D@EF+B 1   +   +  @%7M"+C.#5 $#   ! !#)",,07G<.>--+-7$5)%5:%33&36 C;3)9N-3?B@<=C6GF=7D9-.,'& ;3&!E6848OE3Al64S08=*3B&0",(((#&()*6*<)6/1/#,22.=(B 2[hFon;ET>"<9!@7$M@,A>D6YY6]QO^QTQ<^b2Vn>SrYxKk|0WY"u^&U\6:H7WN7`k>^a@R_?T_IWqJ^j//:$ & "-%'",,*,10182'/)!- $+&'$1,,:5,7<28@DCQ5C;#ND(XX;Xa8GI4A>3??4SRHPQ\BZ=p4Pa1=:@E;>D=>4=8AKY|WH\?c|>ThB^jCOj;Rp7OtH\`d[mKrhmzȨ~t]sFpdHcmE`[DjIQiF_vR`x>Yn0Yh3Qi6Le3OX1JS2CT8Ib:EX9;QHE]E>C;EO8I[;OgQ_pTnSqRh_O`]BWT\tEwHF^DEcIZpDTiEXeSXomaxju킆YiaQWJBMBL75>2*806.8;129.+1'(-#.2) +  + + +  +  + + +  +   +    + +    +   + +   +  +        +  "+   +  +  + !$ # ! " 1'='I+!P5*387'/0*!#!!!$ 1&23"0        + +   +  +  +   +      +     +    +  %  +  +#  +'  +    + + 0+011779)#8!     % '(1!&C+D15M7'=5'%     +  , 3$ +43$K'J:'28 3(&'#     #$*/$=K&DU/=RLACIKC!HJ%>P!;M(0M,44-DH 6M)>J+?T->J1DI#@C;F$:G5ED#ER8LKQ,CA%8()%&)'1"7%'"%10 ?+ !&*,$(1(!'$46#0# # "!!$#  + +   +      & !"!( ($L1 +  + +  "$,DA7XaJadL3L@JS5Aa6Lb;VQZY4P`3HO;S`7IO4%3A,2P)4S-PR1OU/?U4J]?Q{Bg^Ae;K^5Tf4GY8`VIdeILS<5MG,DN-Ce,LH$5%,*!"#&*$ *.'+5),6,'."'%%+.,%32(:>48D4R0GY8H`2>nBB4@?7E?DGF=DA0FF3XfM]OMfMjlO[cIHeJA\9Gb6bs=bvN^pTNsJZK_yLtIwrUoFFaHE`3C* "64%   ' '"4+&>532@05Q*6U59O*3W-5N(2Q)6R47C(6<&@8,$D0$64614=R>3D/@J+6C':B)<@5*;,*'$-! 2(*.G0>(**.!)+! *+.0$,( # )   !     +   ! ,#7 , 3 +H*+%) S$5@ ! +    + + ( ( * .!+-IVPcrTgQ..;42?7=>>2IA8H24B4EZ?O~832k1KK76>>/94/3B49I835*.30C.E@:BHU/7A55C;7H:@K59D43:0*2/&,-&+("%$'&%$ +  +  +  + +   + #  + + +     +     + +   +     +  + + + + +   + +     +"   +    +*;?(R.!V4/4@3,,0#  #$)#$"&#"!$""$%" "$         +     +          + +     +   + +  +      + 2#    + + =A. +-1 %, +$ -,%+$)"$  +%&"C&;&"+ :1^J!WZ.MG*R/ &     + + + + 40#"-% +   +     $"(#518%LF$7E-89).3,00.&"&0-)0("'/!"@/%K;5D$(@>?1- ++  &  2 &   +!$%  +   +&,,#,#, 1 %&!##$-9DQGa#>S.5 97;V3RXs`xM+Q' oK J_,iG K7O060B   "!HHN`f]>\BE@#<;-^U-AR6/<)-G::M=1:1KA9\L4ZP#GG/SI6i;~h+TN-NI.TSIubQcpHTdG_jIBMF?AS48K9>3??1BD.@;+42+8>);:#4;!51&1-%)5%*)#"-$'.3!49'5;*8:,!9+,1/B<99JU@RpYiD}Km2n[1jQ/?=-+54.9-2:-/.738:6DC:GF=O;Li;Kw;gZOq`Hc];[FFQ4LI1hR=LZZ\fUUaIE^:GZCbdHRj;Rf7Ih4F\4Db=AkI[A;ZG6PU24=+01%,+&$-& +!%)"!%! +  +    + +       +           +  +  +  + +  + +  + +    +   +2 +, $  + +     '<I,V4"U@22J6&,7$!*"4(./%("&###!      +        +       + +  +  + + +    +  $  + + + +  3("S.8$&2#!'" 5(752@#7/$21!)&/$ -, 1#91.;4.'4$(814JJ>\Gbh11\1   +   4 :)-+#$!!     !%,/*,50'!0'6/66/./#*0+.@=$8N.$D2(F+(:'!4-#2& -!17&>D"AE73('2/# 3 "$"I9$4@?MG?HR 10&*&!$!,3>3'%   #   *-"=!#>2%8*6E%H2 + &< BLCV4E )5 *'/. +    ""!$)/!4> 5IQI3GK .:'1@!A2c*G[:K#0H+$]@.k@1`(qw:f(6g,<'!# !%")4AAOUNTDE^2V_+;[;'H1#6*"-6$85)*,'28LR5Wc6Lf8__?u[RE5G=8H<=PE]jO_}KGcUIPF9E5AR51<07B78G@?KBEH19A,:;5:E15@&>@&FK'5?,-;#10&)-*(43-<4+,2,).&21'ES-Vf9BDL@BdCMaOib@vL;dN]5FTGSkQTn>Fb+K^,F]eBAdNSyCVhKXvipQbZOaTPXA>JC6KB5H56E28/7G'%4 (5(*@(1'!$"$! ##&$$! !#$%%   +     +       +         + + +   +  + + + + + E2#-,6+8">"-2%(6+-,-!<-(7-2725!37@?BK(7G-=!$!-3E$^1l** +I +AE7@ ,( /B( .N D5E)H(3"*6097.%!   !'  !   !" "&&%%,+:<''1'&%#).$96.5:=2/2*(!''%#$4,(#&10:H6dO(TY"3K5CA2S;,;L+H<2EG9CG0BA,=D/BE,WC0G5-&+)18*9<(48/3@<7SIRnCks[evyQkyYdkArxHm}Pl{QnxFf{@oz-PZ-Wi/[n4Tw:Ie:SpCQkQEaCGX4BX1Gdn7>mV4BA:HUI^RwxsNMPE>T.FaQHb^]l{lAS65?.2=53C90G>=RI?]P=elkbw\`]MCWFFS;@H<5<=.<92:3,=2/>+/5()3,/5/.66/8317+(-%*1$..#&-&#. +   + +  + +  +  +  + +  +          + +   +      + + +) &,   +   +  &5B' N2'DA1&7<$&( &"(0 3*"-')%%''$+,$&   "!$"!    + +     + +       +   + + + +  +        +      + + (&*"*)/> )R9>?'.3+-,$&A"*7(*%(&(*F.)=Q;:X;#d F  " 2 + -?#T84QLJH<80D.=&(60'<"0+$$#(6*!)+-#* %$ ($   $,1'$&/%)$#*+%-*,37:4CF@BU64L0:=L37A,)1#: #0 *7U!L" 7 *         #  + + ( + "      +      +  %!#)"*'$-- *!-!''9.L+]U0%.!'-* IIRF7A*<%+A3)5=$.5+D/124;#&$ !+(3-%.)'-N,,J1/7=>ILWP9]A;K8C?)_O&`C0M=?6=J1?G0D-1:/*835D78>5;?%9B&:;-3?4MY/^x5Xd=Th.d~5hA_=Zx=Lc8?K:=J67K:N\=J^;JW:>K?MVGAS`5Ny-F9R_L^DFW[>Rr8R0S`FJBOK{idkȀyZq`oqi|blwF]\CKS<3>34;22:787356055*;6&(1*)5.052+00).-&)&"+%&,&&.'$,# +  +  + +   + + +    +     +   +        + + + +    + +0-$#%        + +' :J( E5'883,28(!)!!*#,) &0"".%&&"+&,*)/# +    #     +      &    + +  + + +    +   +          + !+#:+IX$>T-$:.&"%."*R;+JU8K&#>(!.J7L&LE" +7A3@'L$+6-I8;.!./+$&!(,$4+ ,''!$*5@-/A;%`N)p](dS:SEO799NH5w^TH3GN8LUGc^\haXpaZrrW`WOoUUO;{l.z^)IM01O13G9;UEA_EMjA`eElm;b`2IUUSkYTZ[[TzVXwQ[P>H/->,-6+88'8B+:I4BJ0IRYh¶owtkj{wfqv^mM[kAS[9=E1:>,2<19=1@:-Q*;;;0-2&'$""$)*$+&!".!),)    +    +    +       + +       +    +      + + + ,'>,I/?:45DE1313#"*3Z(%IbK9VH"08=*=)*75"$3 .("4%/ ,#0B*I&8%-/7/?%H5(>4%&5,&$.)(+%!+.;$4'!&$ %"$&" %  " ! (!A(,7'::5%9@90K#      +     +    + + +       % !!$@8=0(-+,:)8 +!=,##""''#&+0+..=-DgJVyMF->G(+3(#'%1-:95:64?14-,45-.,13(D@LYEWXIY:D^6NQ;:J(2D&1K=9T90D)29)0;<0@K9[RQgAcjDKjTSt]chxdPw>^o*4=(+732<:8<@5HY>ZaUtXSa89H%&;5*KDa|MSvIGn7]Y.XS4MX;CXDKcHWeTUl]yVyV`bZVRB8A.:8)=:2EA-LF0HC9NE:PN`yщȾ|aj|kqhyj]lbT`U[fE[`?TY/KN1=E,8@2BD3hH;sS9N1L<,)2/(8(24%36$-5$.2"*3&*0&   +    +     + + +    +    + +        +  +     +4(""& + +   + +  + &$    + +! +: A&Z1LE.=@:*2-%%#! ""!%##$% #    + +     +     +    +   +      +        +   +   +  8J<$7R53J< <1"*$+#'+:.YDTR!A48$ &-3'''*  5C1<8S09K",?!7;1IB,+8)**)!!+!*&+%-,"(&+%$#$ (  !'"' ! #2+?>G7*:C"@@$56235=8>.44"",'.2!'$(&)3.#$ * $",+$$          +  + +  + +    +  +  +     +              )#"" ?;."   %"$#%%!"'!"%$ &7".\-:c5=-=A*/0"&',%$$'BE1FI84G:65(/$"1,&@O.DE(<8+<>$91'22/)482C74B+7?(_r(,wd1uxC\nE3I>4F:3A@(5*2.&63*5;,9L,GQ%XW*MR)BL->U6=V8@46739:'3B,.B-773A@QLaJK\:@^;\zGwfiyW}Lv9_{/Rr/NA'9+&8-0@1OQC\RE_JAY;JY;DO68E66>=PDHcBEaL;060'.,)0-*1*)3),2*,3+-.)*' +   + +   +     +  + +           +     +  4 +''(  +   + + + +  % )   +% +6 J$L.$J=8?;@*12-#'%"4%$" !,&)    $    +     $         +  $          +    +  %8%-59OcJOY=JK7BKHeNhYZSqHFDDCA8>;'A@1JT3?,45 <978BH3A8UXMcjikoX`sSVo_T{~޹j}UHnNRa=Z`FlmUYiMU_PQS@HI`vi]ujH`oL][:KS4FI:CG6GN:EH5AC7K5>J2?eADT::..*$)+)/*+/(*1(+3(/3+*)%!       +!        +      +    + +   +     + 7 2(+)  +  +   +    - <U+N7$HA7>@8230)'(% 8 )!!%"( %"$*$)&   % #   +!%  ! % "  E F-Z;    +     +  9=@1<1,+(0%QB4C! -!+"+607,    +  #%*&,+.10*!+" .8!4F'!5&()&'$$,","3!%.'/8 3.'$0)C2;.,"$"&*)% )-$0&"$'97$@D82HO0;E 5=(H*A!!# !# %!1&%     !            +   + +  + + +   + +   + + + + +  +  + +       # )  $#*%-/. '!.3<9#',42+a>P34$ #  ) !! $  +!>0!@@M;3+*5=87D56G23D4B;DOCQH@7;A1-:8-6E+:C.95??<>::AE4\Z2PY,ZT597*LA&>C;1D?<7<.<<-?9*5>+2;-2;/1(-1%+0+*88,K.Ib?D^O>a\8SS2F<-A018+7J+?S/TpTBs[>iOcr?k`E@CI5F;(5)+,%/1().'.."60%/1003/21,&41.>19KEBYWRdL?OL7IH:O4DYQ\r\cawsm`q>`|2s4v3w2o4O]6AT4?N>XcEQ_:HS.;O-L])=N0=TCCUXAdbPefK_[;SZ:X<|qHU`FFXZRe]^x`pwXeoNjrH^o}~|o[avPS^UNbINP86::.5;,8<4>8;>87>8;C4>G9UL=sI>[H>sK.A9%1.'..++2%+)'((4&78--        +   +   +  + + +    +             + ! 5 2)(/! +  +   +    % +* " * 6 C'O/ C9)=6::;6*03#$   %=!-!$#& %%'&)*&0,((*#!$" +   +    " %#X,%AT!'+,&       +   !W KY AG"9=/4/'87JPVc++Y&%>$+'6%2;!>-$C-G+   +) 8(-+#"+)"'/$*0*./("6$#)-#!)'-2>=/946-8!*9 A>6@0?%&;%5483)9#2(()3%!& %"%! *!9#3'#"$./ID38 &#"   $7$H,-(" " + +               +  +   +  + +  + +  +       + +       !#'$ $"(+.3(# % 47,6Y$-U1S8@;H!6 "%! #$ :6!:D&265IR+KG&A48I77?1DH6*:2/:-*3-)0.+-/<;9>I7=WBM`K9TJAXDCT,QK.XG1A@4GR-6A7/5749)2:+88,>E&<<%64&31).+()850C>.89%3>%:4/0-*2*37-GM:`PI?J?I_MOoT=mB9d05G237!22%E?$G=$B8(<<&48+&0+%1)23.01:68N4;d/AY@C@J=;78A4>R*?Z7L[=glPmuQt\YsHSsS]VxUfM`=Zd<;PCAWCHb;Mh0Q_:T_8JN,CW=>VQ;BT0GL8KE4A:2EW5QN}]7J:D[MKebKfjUnef{ajwY^~qtYqwKd[KdOGZR=PF?O98?3*110<5:@89;61=51:40==QGFqIC=7C70)24%&)# ! ##7(,%7%!6(/($/+!" '%-*70+2"*'  !"!  +   + 8/&5C,(4       !. '61<QK<>PKB:;925.(0*<<MI,R 11'?K;J!7I=GBnW)!&!% +$ +(0(+$(!!#!0'*$:'#:!3$',+"0 '(( +0'(0$'055&+8*=<6H)5@%-3 '+,9&/;$4:1>"'/%/)$ )&3*"4-7$"!% % ,71%5)0 +, %,"!          +  + +     +  +  + +  +    + + +      +  +  +   #% &#$$+0%<;+&'," ! "#0,102,.$+ "13846L0=SKpt4F\3F^3Sj/N\41I@,=C/5<26&14+")* '#),-(86*?>6CO.HM(3D%0M'AFGMNL`7BF+;D9CO*CL%CF*:?-MG2MI,ET.@F,5B(3:#11*79/8?)35&13)8;'.;''-,1;K4[@8F2KU;EMP->D+540@*6B*RY3LT>Qe7Xb5hg;TP+3;&%.+)6.KM/BF:;B867<;BC1B515*001,6>5?C=DHU_SS_MS[GJUB1CJ,SU<9EMPEO`ARqEYqNl`TdyXwoqxpOdTFTRLVHJRC=ED3G74<,)3.,94>C9:@179069/"+1%11 ,9 !4%"&+&#!3+)5)24!.7%*.&(1 -&3$ ' ""!       !((%!2&,(-)"   + ?RJ1B]=AR8,X.N)$,5/,G5CG$4/&/3%(%%5&(I-=3"$$#!*!'4)8!/ +-* -$+ ($&1"4-(A0-?01*/ :7)C=%1&1,-223/"##!, ;*"(.%   26332)50"" "( #" &3)4%#" # &1%"             +  +  +     + + +  +  + + + + +       +   0.%) )!&$ #! $"&'!1)/%(#"*-$5$)E%D9'C9:/8P)@T$1;;A;3A?/Q,S]*ET:FlC@_EIYB:K3,17$--.3)@H+7F/.<'6<,6E)1>"4<)49LEHcJPmGVb;9@.0/474:3UK6Q=JP.GI-G<=*#-)4):FJgIn;VMKDGmRbI[J494I87I;?E=I8@Q2@^4H^=Scad}\gJifQa?Q_BN]AGS=GN89<3+2()0(+591A64B47?14<2F?ATCjA/%%$"'%""$ !!"'(0%2;&5:& +    +    + + + + + + +      + +       $ ) 6<$$D()C2,-20+"" ! &)%,/&(,%',%,&$!..8+7"!7$%,'2(%%% #( ,& %    +            ""#!' +# 1"    +(& 0@543M(2>&(;36'$$&.,)#!H7:./.$5.#="922#@ (,:!6+2);&(-"%"#&!'#+$#'!!$%)/!&0("2'*3.!1*/-' !"'&$ 1!5H-G6C&7&-+$=>!!  !   )  65)# +/0-+&"6%   +        +   +   + + + +  + + + +  +  +  + +   +       +   !&1+4'&+,#<"+ -!#   &**#%%"#.)7#! 3.$95C1H@/@E2GN6KI>N1BC-?I7CN=EK-?B*:<*:808;(;5%43+=64>?.=P09)G8)@8+DJ292D9 *-)*.=(6;(03)14$10-4&20,'.,!))!1/,:65@C=IX>D[EXIZ_,]_)e_,>E44=@6C91@5:=.*:%0:47FA=Q;9M{qKc]X;D;B;>H"&-%#*!' 9-# $'742C,9:'7O$'C+&'.%!##&%/!1+,-(/--*"#%"  ! "$5G&2=L'.<'G?"";/-"!$)","   ##   4",&"$'*".,           %#+/""&$#%!@4   "*( &!-06 4B3;2?%ST/VM32;.963>A<1;:?E76HA4BD8>A@J/F+GS4YQ1AB:EG:AM0;S(FF!@S/OHcCTi@Xk5\a7^m9np12>-2;'GN/n]@F:VUI\`F[wVzh]qLDUoJckjdN\D1898:CE;RHA=D72-3-6:,56,.4D4CVQkGPWB@SBAQAAR>7HB=K=@K+=>'.-'(---1036+2519:7H?IA?C5/%$ %4:'-:'-<'-5+         +     +  +    ( "#       1>"?+P2P7'<:,1-.#% !"#'2');("%#*5 4&)' -.#+-,'"                   &!.-(<1") )/6'"1(086*+09+)8#*%( 3/";& +""?'6210 >%C*4/2&*,$L&/J* <8" )/$, ,*"+#0%$%   +    :O,<[;G1H4)"2+,(  " !   +G"!:..)" !0 .%L2&@X04D8H7)98),#*,),+/9&9&%,*  +    + + +  "".  +  "'! +   +   +  A](@  +     +        $!'%N6H@A3%5*,$)&&.+.+#   !((-?,3<#(/,).;>1D=EMAIL4.991?=0:BBL<:CF"16",(.0!,)!'(*%.(%-+3.92BJ%cW)IJ*;B1AL6AX03;/%17"0+,D0,@4K]IXfCSi@k~?pNX^_Wq\ygd_RxUGmOHvI7R10E26I-0A+.5$(/"(4!&4$03.7:,-.**(/ )0$*1,,;/0B5DSGWmqrsf{;:?.%24&6.7=*9::;KLFgPVl?;K==?LfaK~nhsRwjLdcd~HWf8B6).9,6D@>DB=D=7.131871:8+/8.F$      +    + + +   + + +       +    +  3 0""   +  + + !$1 6%5)D,"A7.867,-/&##&+"* ,&&)!,)'(9 ," )#,*'&,,"5-/(4&6,#! +  +       +                +   "(#)#/ '5$6: 60.<-)'4*$%( JGE"44!A53JA;%-:#.3(,;.8+ 2#)<+*%5"<92%#$&&  "      " >bL5g+21C!*6%+7,"     !   +D#"*@0%+ '!%"4/,I/,4&F,NC?!G<#8=)5-'483/34)0$"! +" # $   +  + +  7)FU<<4.#  7    + n48N,T,KC>\U!#& 2        +       +$71[e+Cj!&E $2;OB.17:>/BD75:513,75=5>I6C<6B/70.35306305#1."GC"AR5;483>%2H&@F8XXKXzIQe4GQ8JQ=DH6@;(12*+,.9A8=O7.EOFdGLxFZ~QXJvH[w[W}kQsdCcED]/Ib5E_82L*8D >B#8A$,?(1;!35./"66(.4*(-#(3":6%2,%*+&$)2*3:4GH>_vTrigpR[O*@4'C@18@2c_1B@F=N[JQGldEnaP~_hZOjaMY{csMZ8S[:b`7UP9BG3BF,45+5;48?(97*?21F2.?+"""$!#!!)0'CJ*=L.8K%       +     +  +   +    + +  +     +  +    # + +     +7 2)-' ! +  + + + + +   +#17$='F-$<1(532/,*$% "!'&$&%))&0$#('.!33/<%2&!% !% #'4&!#(1"/.&#$!  +  + + +       + + + + +         "&*$$6, 3E1"8!9;#>M+<0%$'JAN*4N1:P&NP"AX&=O/&V3 9+.""1)2EH/2P1+9(2.-#<02 !.)"+*'#&  " !%! >U=7gM';#@="#1% !&* + +   L/F:6$$)#0*3(4<&<0)I0)?31<-96+B5+&8(*?&)2$1.+!#%* #"'.*!)   +   + %"?.7 (?(]PEX?@SR>/( 93 RAFh4)0+'101. ?9-1(4* " %'"#&#&,*#55??/=#==& .'((/81@F=;:4GC/3;848?7=81?,(;./?70A7;H6GD-(4,&,8:A98H$$+#!,)$D.8],`^/CE/8:5=D7Q[=>mDG{R"Fp'3J0-B<'II:GA;^aE~IRtIOvYDaN2G<1E6/>/'9+(5,*6/3E+DD'33+06-17'E;'2:)4<%//"2.'>92<6#4@"9@(>@1.=CB68Gop=j]UacbOfnIii5Pl3Xb=Pb`_cM\eqjrh~OOY/,,'(). .2*2474495:<=:8:2552:B>LWHOZLO`?GW=FQCEQCF_GQ_6GN8DF.?C/?E.=@.8;/>//8.0=/. !!(*!($(%#$.3H,?K+?G*  +  +    + + +     +       +   +        + + + 11+-1 # +  + + + +  +& 2<'N-M5%@<*1619''&%&$!!'!% )((/)'% :20L'4#")"&"( %'# .%$#0)$""# &%&"!!1.#! (6"4+"'"      +               ))#4+6'E-%@6A<JJ+MF06I(?<#:!-20/:=0%6A?.6?)9590=.0)".-'KM25G:,.*" 1-,$17 "!#"(!,&2&0&0. "%#"! # H)XKCDDN052$>()'%!  +    I),D7:0%%  '5.)$*-<+(3&+-+14&11 AC;E()" ##',!45+.# +%  +* ,4:(&= DU)EbMIK8(<%K\6]z&lwY\ BZ.I\CbjC6nC&32-+705aP%?G"#*-3 #"!  &!-    +  '#&+.$(C$CH)>?,=<"6.#/&-).)+*6,--#*51/98.54"03#)$'!151(@9*<7.C<360137-21-0.'*/'*0,0544>*=:3B(JA$/2:I?,;>56 %#BH=TaJH^.CS0ahA{|=aW/U]8J\:Rt;@`>HdOIleB~BU>M#/E"/" (/(=;GO8:JN>BE5?@1E;2:5.<5'3,*.)'&+*-52:>.@/*7'.;3QOBoj.F@)%0"13)LL,MQ6[_/JG-7946=*-/./5C4J[:O~fzys9NM,E?2AF2KnKx_Qw[`v[Ud`$>J80514%'$*!$!  $ ""- !0*#' #'%%%$-!$!)#4,-;"*8%&+'"' $# %,5*3:"A%!6!4,',0'<83A4  +  +             +  +   +  +  %8%3B&1.+(<+#AZ':D2JF/6O6;<$#42.1+->#+2.)83:I.=*4I#4E-5@$42'"5%)8$2%&,@:BF"5=:2J5J?16..6;0@)E7> 96,#)%.:&* +" F290G<&=*&) !#!"%##  + +D%)?87.3*  % &!&"$,$3,52<!1/?/ .!%6"184A/C*B'G-E)F"./*=4>;FD.D4%3%(B8;Bg9[a+NG)A>"@B9U#C" 2CT(QX6iy,. ,,#-( '%'($#',2-6C0,>40:3AN.GG2;=20?(2:#+.DA*MsH]z1CW4C^Obl4L_.G[-Gk3Ou06+-') #/9&789I4JY9**B2%2.#/:-?>VDNT=7H?6HF=RDDV@IR>9NH7KIG^CDR9;M==NC>X@DP/D6+G91nJ/U9#83(A@,9A03>)$-)!&%&,-3%/C,?I/    +  + + + + +      +        + + +      +    + +   +2 0(+, *   +    & +2."B'H21>>816;+*%!#"!$!#%&"%*".#%"!% %##*#'!*"'%%$& % *-'*.&*!)!("#%%) *&%6("#7!%/)/'"12 +:-#0. &        +     +    +  ( F(2:!2"!%0896)()* /7#2KB=9<8G4/E;!-4#$2$4%+*0$1;&?"+,0--3/9,46+:@)*6%/;%(+# $.;BALRQ!Ef#7V!<_6C1C/-S22:",<.=%8"+$""!""#,-!(')#<"    '**2(   %#"0"(!   + I"$80>$+3  $*3;D- ".!!(%(OG/TC.B9)"-"%.)&3')!.;',:0 01 &/+5!$@&.63;02:-32%%$J$R8d20$2-@)%    +        &'"" &#",.21>I.--7((GABCM[3*A(&34-=+ 4!&.#')-*3/#1(-'* )'%$+, &'%5108,;8$60$))(*!& -2*986'0,&07,6A&74075$,/"6(%0-;&-G/7D7Ht5XYeGq}=jcC[;^[33:.*90-C&+:"'1.4(+J,+H*(4D7A 0@/?H5-C;(E$.D'5M*KV,CN+C5PIJO17+.(357F6;C/651*3H,6d1KZ?RA1936@19>-7<59A3A=1682322&..2888@>;M:AG6:I9AHA=HB=F:7;:87G9C@>F9BB8EBDF9H<6@=HYX@kG1?53CG35;-()*#" $ '("2'6I,    + +  + + + + + + +       +  +      +    + + + + +  0 +6).- %   ")5 I*U6&D>7545%)#!%!!!$#+! 0)!'%*!1 %&''!&(%&*#! ($(+ '$""#) !,/ /3&.4/-0,%,  + "       + +   +(36;+,/"(%"! )$!&18&AD;6(6<-7E+.<'-6/6:#,7&!$'#)%0 -&,()!"/?84J1I*FD+'8 7('64-5 #':YZ5Bu*,0"0!3. ,%#2(74 ;-$.&'(!53,.#*C#):7-W&3$+!',+                !&")(,"32-78623&'"&.+73B;;6,13*'5."+"&(!#.&#.!'*,$(/#).""+###*!04*<22C&94$$)#&+#!++%5)))$)/+#*,&-'0'$*$$+*"0$",&#*,,--27=9PEKbQHMQHPM@9Q:6I86K0-D#!8)"4+'3),;"$+(*2/*-"!'&.2*CG'>A/-#87 02'10:3@D;B1(1''1-)/.',9*:13>..)*%071)4:?@G>D;R\'`\2TR5MQ1Z[%>5)&+;*3F7:8/01(00,90:;:68=/88+5,*,('-%1642D:EM:EP9EN::H:?F?CJ9BF/:>88@=BQ>NQ>AH7@K:BF95*8--L@;MJF:D88.49+2'%&"-),%*-1.+-!*,'-5& "    +  +  + +  +  + + +        +    +       +  +2 :&.-&  +  +   ",6K"K2\:*MH25=<$)%# % & #"$$(#)(%#%$%"")'./*.! % !$!$$& %%#!"(#!""%!!$ (#'*&'((+'"-"!  " ( "  +      +  + + &1"$$")$/-,&%06+(I*-*(7,+4/%&-,0'C* & !!2,*%:2)2J11@)(8#'2#12;<:W/3CB 8,*/'+))# '% #! " %&&)" &0*//3/%$'#$!((#F)#+;&<,--?28B8: )2&'*(!7+ -!$  $   !!'$&'  V!$4.2$  +        &6)@%)+"%$%,()#%-$  + !PW &"&!!0/?;B%J_$h]SS3U6*#53')/+#((#;&-%)1##       + +     + #!!$ %(1!14/:623+("& #*-)7,2++2,/ -''#"%#4*"#$+($#"$!' ! !'$&22/@(/-$%$!"##0#'$"2$((#(' )()!$%,!#'%% "%'%$*$*2-+<,3F9J<-L??R:>H'3>'5A'1I(-E)4M!7>'%%#*)2*(/!=;*AC(17$'#%$8%.)9**:'"4#*&6"%4)!.& /!!,#)!")%26)MA2A?(13)/.+*3%*11-535@'AJRM[rK[EbF`yFB[=7F+47,8/3-21'1+*60,644A:392+.1%**','--(8>6/=8?R=GT?FM7:D:3C>=G;=J;:?53;<2AC/FC2><7?;;@5D2+;(9A4FA9:524()2!"(##",$.8&5=$3LJ87=B%,'"%#%#&'%!&')",.#-- #,)$,.,5 18(4&,#'$' !%!" % '!!  E"%;' $' &,# '$ %             "#,, /$#"# * ,+&*! +=)!;I"BF' C %%'("&".:5>#:(&&(:':@.I#&>3.0'''!,) +:(&;9(8*95$$3!,$+0&!&'(,.0/71-8"3C7 CN!=>!051B!<3"E?'1?&G:*>96&8-10'./.,&2;1((*!(%#$)%-#$'" %&"('$$)" %#! (()/      #&/#B8>>4/")(.))*!##     .& I1&204Q!8N)D>2E6!'$"!/</770,5&")" '(%/&)  +       + + ! ($&$"!(!#,/6A.=B'11"++)1$(2*.,(74(*/ $!&(*"%& "!%'#  &$+1):-/5$7@13;'%(%&&# ' !"'&) -/ *2#-26D#?SdOA2*/">B%E7DK::\:Rg*Wh*3>/%5+*-'&:+-G/F4>IF#LV#CI'9B0+<7,5 (-&%!!!&"&' %"' & &&#!$&'476NR5HK.6<'72'0."*0,29/4615E,P^,M]&5?&26.5;06B.3=.6F2CO5DXDYgHAWOOkSa{SV~QeDgc1>H/:A/13,42'55(073,69-41")$&-#3-&56.6<24F64B=>KACM7BH25@;8@73:5+53)31-/5*.;,.4(06013H;/fH0Y8:C24/)* ""! !$"#0*0,4>-1F.:J1=N5AP7 !      + + + + +      + +   +   +    +   + . 5%"/1)    " 69*@+!R/,U9/OG<0C<(*0& (#!-/'0=(368 ) +*"66'0>&%<'*)1' ", .4'3#  '&8&;#72041!*    +        %!& $%,&&,3,E.60)7+?27(A.5:" 4G=-R (,#/!-'%!"%$!#') +0%$,*!"""$2'#,'+'%0- &1",'' "!" &!! $$$( +.*&0&,:");8())(.4!2B/;4-Y37L/-K&*@5&"&." "#"(*4+!#7)!!!#!**)-$+   ")$1'()*.,*!""(%$        '&!0J'J('#"!1(>".463+  + +   + %""- " * 5/ $("&)(3),(0*%%-(;1**+,)*#,+ 1 8  +  +     +  + + + $&'=&'!#('/;0>4HS$6;"/&' ',($/9-3;!,3'($"'"" $!%()#00(5B.==0(.?(3+&"!$%-**/%("$'#(,)*2*#*!"'"):%HA!D(('$-+110;#:.)5',7/3F7Dg4ao(SV"=ACHIC<9@:)FJ5<\03B*75.-4-'3$'/(0-.,-+--/$'! ! " #" $ "'&""(0,?A09500&*1+%1#(2+*815E/4D7-@;&2.%1(&2*,70(3,+.#13*=C4DBInDQc>KQKRnGGXN/LVAi;>L46G0/;.261/83.202=1/2&%)#'-&*0-472+:%41$#$%!("%#'+)%5* "!&!-,/1%;!"#) " $)##1 /'""#'$'' "%#/0("8:1!%//-%"2$$'D!>6(-2%7%#( :599;5!80)$ !*.&$.)%      %@%"&+$#,( $ "      +            6,(+(#L/#       +  +  )",- &) #!5"$!*%$,4+90814RF7h ??D, (  "       +     ",#2F 5H.1BH(BG3>=;)8.51;495$/5,1/0,5 .+*&+"$!$$&+7;&NR/@W::B59?(72&%)$$! %"')! %$$&*$( $"#($'%<!(+&'&!*%"!% $! (%*246OQC\O>Q>(55(7<1G6B`.`m0K\03H+09.44,*)#&('#,%76$)1&&.$%*))1!-1%+'("$&2(, &#%""' %-!%%(.(00(&#%"-"(1%./&0-$*(!'%)()""1"%"*';/;09A9;R;=MF*>O(;5!,J#3K#75$86$12.1&22(32'10!.(#%%#%("*-(4.7C/>M;CO8@N=@N69B+48-54/47-.1+*4%+/)(-%$)%!'-96=bM`qegD`D+0*&*1+>?'EF(9>)6=+/:)(+&",-(3')3& !    + +  + +  +  +            ).!&, $  '0 4*5'!:,'W3.CE;7AC(,*$!#*(&%&30.4.; "@ /$ & '7)F"!;')-&'-+$  & ,6;).9# %  &!       + +     +  ) $#"$!#$ % %%(%"-6&(;./=16<.$+#%5-&"#('*,(&&9% %$.($#" %- $"#$#!##*".+%#'#2+'+5'=:!$ &)!$, $# "(!&*!'11+ '*$+"!)%$!+$"#!%!'!2&+#&"0>'.A1;=,.<+&$$3'6%(!#''# ) ,( /(*!+$# 8(!!!#!    +   +         +     +   1   +  +  +    + +    "     &+;")&$''%'*,8)!2C.>$.."9(3444B+    + ! "& "&  &.Bf#Cb793#G;&76;RT6FL4%,")/$0-!6)&.((**0*(!+&'0 )*+. :8+2B90:Q&:@ 183;24:1' (("!$ !!# &'$& "!*"* -Re^W[DA,""%(24B.9H+2@%*%2&5=;BOE]R,J0&7/(9,(,& % %&*#8(1;(#'%$%'!%%$"&%!+51 %)!'1!%&!! !#!""##!(!-%%!$(-"4/+(*%'/%)+)."(($$1/,,')'5>0GX24N6%@3,32&0,+('(..()*#''$& !*'*#((#+*,.#7,%4)#$#%#'1-:96JMM?AE2<@31:288-/0)(-()+&)* %&!##*,-EJKg|gwaG_B1,"$!",(127A19A,2<-.8($)"#$#1!.6" &"&""&#$($     + + +          16$#.' '  ,,".",#4$$0*)66#%=!+$#"&"!#+(.$'+,+!2? <$ + *$!&.(8AI!(   &+!5),!!3 +                 ! )  !%)(0!-.)33%,;*3@")5+&1!151>OGAS*NSGMNQ?NDICG>B*)%(!..+#9#1'3*#  ,&/=$@%   (!(.&(($+"**/%%5#D/$ ,,%0&%+-3') #)"'6*(!" $(""* '4+1!!2!."! (" +"          + + + + + +    +    +    + +   +  + +  + +  + + + + +  !%"+D</D %;#2!) !'#!*)'   +    " "*B(K<97KO,Og3Jn50KR)79"%4&%/-&*(("'&*)74"+)&5+"1'23!@C1HJ9MD/-:01:.")%69- + &-.-'$:*.# &"%#$ " /%L<>/m;L|$KZ"Vg)dc-B1#& FE,DWD6S>&/$I7AM2#0,$)+#+%*1*04&%(!!-$**+*)!!#!%)&.279F/66$1+#,(+,.3.=:GZ?SV7EF7176'1.)/'&+&%*%"'$**"-)(71;%52.8#))';6'@3*;((>(3-(#,,+,04")+(+#*&0#/)!"       ! ##$"2%=3#/  '-#"$(#'3+%"  ##B58"+!   +  +  + + +      + +   +    + + + +     +    + +  +  2)# '!.%" #*4$1&9/       " ,/".49LGLMW6=J0I32;#LQ>Q$*2$$!#&(+(+'/&/2-/=#;>+PJBRUCDK8-5-,:'2("#&#'(''+%)%'%#(*&")+$-"  #"%%%%!!!"!*A!(I#/3#=./O;MW%@4IJep-Pv<4N,10',!(7&+?"*:>>31#.#, !+#/#+ "/%'!!  !!!&#",$(2+19/3;.AC.KL05C1NX7hc4RWIJh]`z?S^7BNC*72&-$.2#,1'*2/2?(5:",4%29$+4'0;&-1 ! ')0667;926065(76*:>1<@==I@=FB9FM@TBMP/63('))(++&)#!$# $$)#23+N96TIOZSW[MNLF197!39"BE#HI'CE$AM:1>E.++3799+9,)7'"+!)# !"%##+&*2037<2(721@C1LO"9A7H-$3.08"3976$88$!5(-2"#' "!!*&#"&  & '!## "$.*@9A-LDC4S<)@*'C)(6 !)$+,.!1<*A@*1>%8@63!$*% #%%!- %#$!! #&%)"*$""$ &(-6,=46>;7BDFY@FLE=LU>]ULaRQ_h_pcbpEEA932*26(4;&19*(:1*72'8(.:+*51+9-1@+/B)1%)*006589;:66;8GN-B@/:H:=I@>G=F*CQ+FU%>C%7D)BR*=J#AI%?P& "($$*!#!!*#" ##!!$ +              +    +      +   19)!3/&-   +"%.),@D*<+..%  $"!' *)1(600@(>'%0",(# 1#,   !)+!," #'$ !"  +   +                     +   0*/#0/%2"'"!$#)#!%!"%!!!($"($(/.  ! # !$.(4,>#3>19)& # "()'1*$89$=!+ **4=>"9-3% .#!!#"$& %!3&.#(!!!! +%##&,)2=$/%#,&#(&6 %.*1'1! !  +  -?7E::+)*#'!      !    +     + + +    +  +  + &    + +      + +  + + +  + + + + + +  &! &#    .&#"#2;?6"wW0T^/;N*6+#3,+)-'$/%()$'##$#)-%(-2)73=7)7B:,3:'#5"7 *)-.*9,3H*GN,%' "!#$+*!! )(6.'*$& !! ,16<.27-%:)9C@:5A/))!+183!22)-#/4%1).>43H//9-'2!$()*!$#&"()'$',*/,/" %&*"):%67+1I4FA>H;*OPEQO0NY@dbJlOI\5EN,]S8GF,MR,J?+08,!5-'4(,,"*+(7-.4$'2,3)0611>68@19C79L9@P8RP5QK<:F88CE@SHNS=AF14B07;?BIDJ]IBZC7E1%++&>A/?H)@G-)   $&&)    +   +  +   +             + + +     +"=<7#96(. !$*!-%+!?=-<181&,-&!#"   % (*)1)2#7"+1% 4&$))14 "" %! #.%*&  &3+$1!$"     +   +           +  + + +             !'""%!&0!,!$"""%#+/329<4?793,+.-!"   #&"$&## "$$2.%%< 8.C;MEGJ.L"3%*0#&5(!,./&"- $$!/ ?%8- ++$%1)%)%'(&01033='-25.*5!;6"-4&0"/! +   .$<5-30()%      +  +      +    +   +  +          +    +   +  + + + +   +       +  + + +  ! #   +      ")<:!+##)&-181!7^)8L,22*2#!*%')$.!%2 -6 $!$!%''H;&Q<1OC5066&-% *'$,#=-(,#.*+E)0>*-"#.'(" #"*( !!+=:36(&!,&%5&(2%.$4'8)0*'9-*C(>U-Ti4bw<2B(2.#-1&8M+>T!.>&2;$3>!1<5,8+2:(-2!!*$!$!!&"$$%*  !!-'$0%*6 ./$(!'&(/2+/00!,<&61$,5':F1G=1F"3T5LJIXBASNIeIRaGPhE^i3IU06$-&$-" )#+)(2&,4")7'*5,-;5)96.>03@C)CF@WJG\;FSF19C6SG8tPFjKAYA2:6'00-05/+9/0>)3@'6E)7B)8D,;K(7A#1?+  $"&#$#$#%#     +        +         +  +     + +& 7/:9!&65*/% #('$.#:!@*>-:/(00,(+,"#"    ! !!$!##"$)!'')$,*1&1/!%##"''0 + ( "   " #"8,C'    +   +        +  + + +     +      "&% "!'!   ,$24"0;&,@,*?%2= 3GAQ3K)2%& &## %!)* $'!1'), ''+/8!/I/'H8/?27=6E3! -*"7!!'%& '!"<& C.2$,&#"%" "$("'> -1&?*41,;"0C C-#'-!"!     *% *3 ?/;/-0/.'5"#  +   +   +   +  +  +  + +  +     +     + + + +    +  +     + + + + + + +  + + + &        &+1/83$;3>=,3//5,0/6-G?&9A&')#)"&& $'*(&'&#$&+ -1)*5>&@< B@#;,41&%1#"("",#7?-\jaR'e>/^A RPash(Tw"$' "!""C30,&+:309("+%*1(26).2&04"**1&-,'(/*%42,B 0PBQ??R*7:"=@+/;;-C01>.3C/.F*+>(-=&8B$/=!(% *#( ' #* (/"%""#$)%"+))" )+#*")�+(8"$/!05(-.$(71&'/$)-7/8A3QD2LF@ZXH_QKcdOiMH[/2>*3'2%227'31*+1')2$+2&*8(.70;A+FH*GH:8NMJZHJUAL_ADYN=PDGO2>H1?P-;F)@J.=I47H79DA[QLiLrSBRB7@<-AH-?A,99);8(64(7;+49&17A*J6,>0'5&')3*&,'!)"&"3,  ( $(!+0$*'/879*:.( +    +          +                 +  +   + +  + +  + +   + + +    + + +  +  + + +   +    +    +  + + +$.JC0Uf*Bw.PZ ;D(9K+,<93F/00 +3$''(39 *3-4*0 2:$'20 %8$8&)1&-0!+%&% #$)"79;R,TIDpJhCz8rB3;F4FJ9RMEXI@M).D#",(*%!41(7,+1"$.'4#*4(+=69E>IU;BX1HQ>=S?7PF@PA?OB5F@@M3>K0=K/>O+2B(:L0CI0CIEe^[oWQX%*1$1< 6@!*6.5"!!% #&  + +   +   +  +           +" $%5(;1"/4-#-1(!" !%.*'#)!$*"-'),'6#$>#E)'42%2/+*+, #":D;$B,>.8913# .!(&*,4(&5%/'# ( &$  # ()-"'!!*%()#("1&*&, +       + +   +     +    +    +  +! +!$) $"!$& &&$))&-+&C=CI6F./)7/:((4')1!4%2".9%<'(361; +8#22.6)76/'&#$$!    )#%1*%;.<7 O@KW(-V)M:>J:/6W4)A2+B'!@) 3+8B#7G(.)3%47!OD5?&+   ='=!)(,)2+4:#$; %914114'       +      + +  +  +  + +  +       +        + + +   + +     +  +   + +   +  +    +     +   + 0.%;DE8B]47[->C7@41?5)6/7E45@&19'!)#*$$+,.4$$8 )1 !0)%1()!(#/" ($"*-4?CJ#=A/..A#,T,S6O9+:5*+.#)%-"(!$&"- #,&%)% )((#'#&3F,DN.=V);S)+6%'(!%'"*2&&."'/&)5''2"(5!)5,*&&-/)-(/ $(!%#!$ *!!!! "!%$.)"")*'$(,)-(-0((1*06(+5/(834C80C>?O=EM(=>67,( $,!.(#+#"- ,!)7+BO88I58E2>Q.=E.4E,->'7B,HZ.B\DLeTriK^Z5FE&./&).*69-EF+EF0=C.36""(*5%4?!4?";B&  !   +    +               + #$(5#@2%0:485!%%$$'+*+#*"*%/$-1$(.'/#H#G0 93.6.*1&( % $C >%4-<3 *>#&2+ 2')&!$'%!.'0'(3 %. )#  ! $  $  ("":"-%$("&(.+2$./!4 *)),$ &+        +  + +             +#&'0 *"-&"!#(>*3!/* &F0*;26$3>0*  06'&<>&9' &249Q?3,C)%%B-%A:#"G394, 6%4$7/, ) $-),&.#-&)'",((23-84629>..80%)116'$2%2&2 )3!"( ! &!(/# ,!"*+ #'*5+2'5637<64>+-:)$&%,#,;1:@OXYxwK?hr9CH+24!73"5*_> C6#!%#"!  )&0.9!=J8V^8:K-,3+&B!"1 *# # ($#2%!-"%7$$.$ -!!)"#/$$+ %  !  $&!*, *("*+/4<:$7B(IN$1@%D&5JA>)%+&-%(-4+6.)BEDWT?cEZhIPUABO:?P4@N8>L28I16G34B18F)5<(04(-0+6>1BI;ZXSfbAPP-36%%"#(''1+3>7;L2GR-GB!6;78-8*6D&CT+"!     + + + +  +       + +  +        +# -&,//$#3$:5+A213),2$'0# ,((""*1+0%*.'6!"B&B3#/5+=+14,%*), +;! :*3.$/9',;0(8,2"&$!"8$06.1+0%0 $+) " !!" $ ,"&1&!,/&26,G+875-I'9"%%$(*"--, $      +       +        +"      !"&  %+%B0*))''&-#"1#,"&%! &),-..#.+, 23"*7,.3(4=&4D(2=$*,!.1 %9&8:5)C1$!    (!= =4/UR)SI8a"16/189D$% % *4!)R0#<6)6 Q ;*))%! ( 4(2P"@$7;9!/!1.$5>, /HA"?B1DF"MC/8##'+&$0,84'!* $"   +         +           +   +     +  + +    + + + + +  + + + + + + + + +  +       +     "" #.5.7@"+J+->56<.,5&28-'-76!35!:9#.2-/51#(),1,9%,!&'!,4"JZ(nu.NO*19$'&-$/5%**:BO{jvj_au;{e#co)1T:;pH0g'#0  !"! +(<*<=%B(4:01AG2EY>QW6ND6H9?P2>J1-8,7>-:B/3;0/A20E->G(6;)/,1CB?{]KhIgH0)%#"")'..*824E3FV'NN'MU&KP,LU/PU1  #    +     +                +%"*2"4) $&)#3/52 -72*6   + 3*,0'(.( **&0(2+"3$"=(;.'-.-0#*.&!'"!%#29+36#45'85,+<-&<)-+$)*)&C(?5<9*/7#+:""A +#!""+*%" /)!'*'489 .: /4.9.&/ &*1+(;-(*.%           + + +    +   (% !" !$%$ #"#!#%**486!%= 4)"& &,#(,*02&'4#6=!)C#$'.1&+0)+3$.$ "   +    $&<""4,-O)1D7/,#59$F $&&(##,"#%&# %#1.')#*"4!&*$0(64()7PC17A:,D)(:07=C$?*2#>&"8% %(! %               + +      +   +   +     + + +  +    +  +      + + + +  + + + + +  +  +    +  "$&*3*8A7'1< /"2 +''2%0:$&7'.>%9?$,=&.9$'8"$!$7 ,@').:4427<$1BP6RYIl.;U&FO>L!FK!CL#ihHedj]j\_jk}BxV:oW1Yo)WU(EA!-A#[): $'  ##' -(")#5@),/.&'-!*&+&*%-#,#-&.&- '!% ( , !+$$ /)1') %!  "*%!!# !("5$)1"" *(SR3CP/JS)93*B;9576+.<'-2*5"(6*/<&9;,3*")A.KP2CK)@N*5A/1<>3DG+=6:B9;J5AP-@=+-6)+9).;():,)75+>8&5,-,4ECKg_qU]B,(  !" %&&(,-6=44=5>I1AW2=Y:Gb5FV4!! +       +       !  !+% .0" :2*>-"76.$3/'!(!'"-$ 3./+*3%/0'),!)(-'<&:';* ;.#5+'.%%-% *( :?'4-!71(2;(-=.33,%5!)/!2-968?&FC.+P*C%#()&#  !",(     !"! ""!'*.,.!:#.$&+'6"56#'>%+7!*0);'533 )@(.(4$      + +  + +  + + +   +     + $  $  / &-,( 1:,  &!"&)0-=$ 9('&&'!)$$+#!&#&&1)"--% ( ( ! +   +    "6/ 3Q D&1%#"-%0%(3+!$'72  8$'82>(S2!+i*"?&$+&P&"}R _M25?8)3$&(.#2!2%#"*$     +         +       +    + +        + +  +     + + + + + + + +    +    +   !4)2() ,0.1(,&/'$.*'#+)'8)!(*!(&$%")*3%-##*+''#<-(=$+@'8Q.6A:7JC7O4-=8!65!95"1<'FQ=TGahDtvROdJQL469612<:37,(-!'# >,8,5/!50&(!#2'-'.(3(+#*@/0D#(8 ")'2&!-# ( ')!))!#%$&)*$$/ !'$#"''%$*)HNHHVO=nj<]]BX\6\S5?P0@J%&2$(1!(4"0? 09(#8"*8#(*4-C@5L35?-:C/;A-(/3!.06K:6B4.D;AN%'1+",% +!%. +- *,#*'(.2MASgnlJ\@%-$&455514%20*0?-;F3=J1?L2;K8=G52H/ !       +       &$)0&6!.& 9*;6-*A.+,*/'8,>6I9?5 65)31*:1 (5 9* A(?-%>+&6-#6+&**!)"(##&B9+60'3;*:-,0$+% 1(%+(& *-'03-3=,!D251"" #(&    "%#'" "*#++-!/(%' &2,7(+5!-7)/!'4.;#.5 /(          +  + + +   + +             +#,&)+*%*0,=;$" !$&%$-&05!<-*$0,%0*.*"1' ""0 &$#"!$*&"0    +      '8B!*G-%1$* ('+'!#')!*"   5)-K+)(37!?# 4!%s.Cs+!-"f-fV[XS097  ' ' 0'(1(!"/     +  + +       +  +    + + + + + +  + + +        +          +        ( '.$165=-/&*-#09 0&0- $'"*(0.7-&%& &0("76&1/0447C1DG)28 )+'+ F?:G\C7QM:H?4JFPn<\_0\h*Yz/]hK==@#<_7f)jw,NL+4$, .)"2.0"*%!'#'"&")-",(%#"%)+"%""&%!%")!#& !!#*($  $$')"& !$)'!!%4!5A4H1UbV[]|KevRd{:IP@5C);;& /'.=*2:%*7$4:+25!(#& +7 07-?2?R<]],VI&3-/25'1E*"H8!J@%F:,BC-)?,"/(%&& )'2!#@&3.$B)'='%:+#1)$-%%"(6 2+/.+&0)(&&($$(,)!&*)*--"',(")++3%!!     +   ! %'*'$*'!%!)%($2+),#&+"# "&$      +    +  + +     + +   + +       ""4*4969$(7"!&!#&)$./31,?/@4%# 27 $#**%$%%1+(=*>&6"7-"  + +  9&&(%  -##2'!"%!*#"+#;%3$"#&*`D&Ek \,#mnb`]9=6 + #)5&    +  +    +  + +  +  + +          + +  +      +    +  +      !! +,+., +'#))$%,0")4)$//-1"10-4#5'0"*'%-:H&;AAB.A5N$5O!&;$1!')#*.*;H%Y[7[_3LR FC;B!Yf=WXG3F9/:2.F78HS8NH1HN/9N3=I(G:*I07FS/Ec?cVHn3Xi_X:6&%-(#!,&"% #) -@%=H3B.5&+% $&' &&+$)(/(+$&$( "!! !&(%!##%#% ## "$!,0O)d4^.Ug-FLCN19=+88&!*.5D-69(2<'4>"35089;A<%,-A(AU=dSLi6LY3WfGXsKSdHSg:PY&46&( "%%* #''027YBVdSi@HT;;.($ *'8<(CO(FL'?I'>C,38-15)$/$)%)-   ! $ $%!#       +  +! +#! !(2&4340,5.0<..J2[<"eE-:K0!85-- )#&"7"9&4- 9$'A+;/%60#+,"%"!%/)9/=3&?< 3>&:!(-. (+',#'* -%$%    +    #,&#, .!)!1&!)1 ' %#  + +   + + + + +  +       ' -%  !#%().0'&6 %6%&,).'*.)#*+$#*%* %#$)$*)-!($!)+$)"%-2'5G=# "1"*    )&,&/$ & "-0$*:)#7k=*=Ak`\`>K4)   + &"#')4+,($       +   + +     + + +  +  +      +        +       % !   %+!,14? '< $6%*%*4"6B)87)).*'%%''&./,"!( )>0=S.5G$%2%+3-!0%"$(-%%/ #/+9B7>ZNIwMUw?Ha2'H=,YW0b= =-)5(%//)65AG23=2$4*+5,;D3:7F$%6,/+2#)#'$ !,( /"(""$&""$%%  +"7%+F$/;8<3/:7BS (,%((5-073$614E0:H%14.0G5;O(1<+./F5=V>LF4F?;SONl]arHDRE172(, .*)+'+!62*+%648]JNrQIF,:%('/1(@D)HU*C!&3!'C0/P,?J';?'49)3B*5>)3:'4C,6G'  #    + + +  " !   !#" '$&%(2!%.&(71(A4'F4)FA0=B<%9<#,)"43',)8#5$2& 6)#/''%$!$!$&&.)"8"%84"1=(,.('')")(0$2+     (!$"&"   +  + +  + +    +   # (*+/0    +#(+/!#!%+$"- +)7<<5;$>)  -*$9-((2 *!.""%)#6+4"#! -+" $"$% % &!        + + +  + ! !! 71('.&$&%(',/%6A(     + *3I@Tl@RE113(+/ &     + &  +     +        +  +        + +      +      +   ! + +   "#")  %)$3;/948 0-,";3**2*21".1$-/$+!%+$)1+$)(,0+9A''3&)6+&0!"""'&0>,0>$-@##!$,,.*7".7'%5.9C)$8$02*&.0&9`,KM.N08O18J09[:ATK0H?"9/+;!<>-1&#%**0!!%&10"2-=2 *#/"!!!$!##  &%+ "!1.%5<);95/@.#(%#&',,++;0EVC=XP;N-<<$7<25EAL]3OI87AV:ZMMfBYi:`\>4K0/3!&*+)//398ZJKa\cB\:$1%.)54*AO&6L&1;&,<%-;%'+'"'$!/#%*'#)).%       #"   +  +! &!!#("  $..('# &$(.#//4/(>9#>=+==*:75)31")#&55(2&"7&7'0*#-$!2$/")#!%$ %(%)*(-+%0*#3'3&,-(+-.*21)       +  ! ('%(   + +    +  +        +  #!"%!+" /%*)!.%"!!","(#!,"B"(5**6%+8(-&/0:?!*A+30! &!""     !1+ -! 6*>=!76 ( $'&"# *2% + %"     $')&?B3FO90*4 + +          +  +      +  +     +    !   +         ($ !(3(:02>+.1 ,/'(70&0(2G&26*)7#*:#1!),&(,.3A)(<,.%*&.-&,*%+,)'$&&$4*$/%" )""-!+&,$!*#%#(5*HJ 2="#.&,5"/5)-('&3"7!+!&, 91*  !##$(#'-817754/B974+07#11$)02,1-'97-;?>?JF:9M8<2=6+'1+ )? 3F/H:,:N6QKAPGOW;OL76A4=?864;/6D:DLDXQA\F58<==J;R[+cZ%A6"$,./7@[Ka|cdqRHeD1EB*AL.+*?%'</%>55:<8! *0) D3 .8:6 @90 $        !!(!)))(=&!  '"!       +   +   +     + + +    +   +   +  +  +    L:8nO <' K\ UXx!s'j^vxFP +   "!!  + +  + + +  !"%*?3.6+9K/AD-?E-%6.'3-)>. 3)%0*&3%"4+*"+7&,$%'("26/=.:N03N(-M12W.%2 ' -'#)# .'%3$'4+;'/$)];KY;GH;=Q$'*"1(-.0 /7 %2(.!LPGDGD%-."0-)%8@"($.+/25D:'`Z*`J+>ABS&))(81GdQa`]cOIcL6dR-MC)29'++$#!$"#%         %(    ""$&)+/$%+!%$"  ) # +#"' %" #"#$  ()+!*.$*0)+,%-0 &1'%/'0-'/-$#0%"&('"%( ("%&$$)## ( )%'%! "         +     $"        +  +  + +    +     " '%("$%,'/.,(1%%,-*5%#2(3@(0*!$&-'9 78/7!-/!#($&5**;APB -S$%8+/+-1<-#0+.1.'%2!',$ $ )   )  +   ,/#H,"U,&-( &.*$.-!,*&$#$/.*\)&1%"5%-,#   6 +/ +   '! &-. 0(& 1&.! &  +  + + +  + +   +    + +    +  +      + +     + * 82Y@x13EP$PJSq%8M64-.15+1+1!.<*,@5Kl(BP0@T:?P&ME&MQ;8++FI+V`@G]<\9FXHNc\Wt;ncJ`hMjpXLbdYuJ;QQJ^J?VJbf[J_B2@*!#! "#!,/&.C%6),05-9 (/H,$%3 (/'"6''/: ",0''5 " "!" 4"*(2+7:- +       +      +  +  +   +  +      + + +    +   +   +   +  ";-C($0><DT3     + + +        !#00")4$,/1 &'$*%+8&5$*4"6LA659*5 &+%%#!"#%'&+-3!2-+;A+OJ>:*0@*2;.+3%-(& -#%(&5<1Ja?';3&54$14-; *9-.') '#$-"+6 "''3#4+8G!FL.= 77.2&>H3bf8Na-ci-Ya->[/]k1t!CL'/M3=\(*9+%.2*=7:D25HENqAd6`u>4;3& B1&T9'*6)M*?^5H`F[_Ph[QaWBQZpwEfaB:RGK\S:PS9C?'"" !*%/:%(1//;04B3=D./4",2#41-.&1+(1..1.+=80F=WklxMeI?P:@B1.("'%'%#" #- !+ !)"*       "  "  $ %+-"  , &$,$&# (* ""$1#/518&.7))5*-.!*/#-,/.0&7#=%9+7,:(B)/,-##""  #% $)        +  +    +      6 );   -0C8;4E#5<^8IM9F,9 +)%,(# + # #&-),3(5"9#' -%)) "!' !(++$"-#*% )-*30!H?$=L":# " #%$/42"!##*&"'5%$())&&-#! )%/'&2&)#)  "! ! + 8?-P N19 1 # 4 ++8 $6 5; Z$41#HF+G(#7&,/")%"556G':B#"#!0!1&"   +  + +   +     +  + +            +  + +   +*,   +  +   +  + +  +  #&(%!+$!".(8#.9@76:!6:(?@%#06'8.C>+8@')/$&+%%$$*-'16+:G2[m5Fe-CX.P](`w 0>27CF=6#)!+449V9>E!36##,.1 .4""3/3") )03 !&&83 +-):?3XX/ISFKZIKa:KE(5-*!-'$ #"##-$)5$*4,0>81A+5@$28%59*97+44)91+9..764E@]rf|yV[A;Y>2<)"!".,-&%"        !     +    "!!!!*+'!   / %%'$+!%"'."3-" "&'&,&,#+1((1&(,"&2",+#7*D7N= F<77&,..)"%$!!"$"#&'!  + !&$%$)" $#.! !                +    " ! '!#)"* 3,4<IK$Ab5>Q6JX+hnL1dEE9**%$3#- ! '/)B4<@$-)+ &! 2%-H(*# $#$ 3   +   +     +           +       + +      %    +   . ()=K;6Q> )  +    +     +    #*-B269#>D'DP<5 )$,*-*<<'2-&2$$0' )&' 1/.+3:3GL8@:0=;AR=Kk<5Z 47E(4(!1@$DK"$+#*!/F1;('!!*(&0;$DX(=I$7E;7^5(H36HD-G1.7,3:)$%%.+4B;K:.;F6O@F\4Dc1)=,'?3"18&6;+?B2B/897yqUrKzI~7rw,MO>JC/,4485=NKJ_Ka^UMbXDdYHfdYxw_sW`b>@ED/HFMr:fl$OC#GCB@HH822149"5A$?G.BG+@F/=>*98297116.84*=4-:2;THerzuVmD:b?2-##&/-*           + +   +   "#*%""%$  +% 1 ($, ,#/)6#4)4/1/%+#*##'$(*!'+#!-$((#&+!,++2%42)95/20,+)3'!#"!# # !)(!#%$##!"'&"!        !     +    +  "" )>%'FA)(X=68I'87+J,.g09#.400 A$))*) -).$& +! 5E="0O*:,) $-(8/*#"%),) 2$"%&/&)  !(%)#&$+&7-&2! 6!*#-.#   " !&*%#   '%%!'&    A%Gi*KX1>>>;@A2I;KUEN(%72//!*.#$, 1/"!(&#*'%$ '22;,J (71#)&%9"2@",,2 IN-RZ-WT+JR,"0/:=([Z.EF"%,%.3(*"&%$6.#4%*933?50<2?J 38.0)42;&5<(63G/W`3i{5mw:Zl>FN<@P4DN?KcQhXg{Sdl?M_4BH0>L:GWPY_UX\D@L@Tf>?KQ6Je/RkLfPRW617: 6Y@ZSbtEov8=K46H5=P+KO(=?'9>):F4BN7?M8BF+7=.,0-.-)+).4:,<:=p[dxvUW;a:-#!#          + +  +     ""&$'''! #$ ("   ) %%-$3*+*#/(.2$0'*))#)&&#"%$+)!*&!$"#%!%#&*$,*$4+&5*'2"%D"".# ' +&+4+%%#%               +  + + +  + + +"5 , >(8\!3O9%7=*+)/B-8>3 7<("-0>@.2]+JKLBP0/O#;;/=A"6H 7    +0% 4%*%2%*#.:"6."('!*1=!%(4A7!;#,(&0.02%'($,#0$C$(9+-7''&4&.-102 #.!/1 )45) !&!      + + +&'   #/   !+  3&< "# ##!9?/>=AI;!D->?#3/"-/2#,5 *'4 8#)'%)%% . .   + +     +     +     + + +   + +  +  + + +     +  +    ! +  !     +   #"   (#$)#'2;<NW>?*168/0(!/4GZ,Pn37N37B:@UD67>),1&+(&6/,/,356Nfdvvb`D?3 ""+/,&     +     +  "#$! ( # +))'()'()(,#."'*(/(%'- %$#  & $%+$/%7"7( :&(3#(( % )35!5$!%     +              ! $ +"" '#( "(4)<+(%*#$3+51.-$G<(B%3+".D$84-?@$BB.C3"@1)?5+",(, *$. *0.9$3*#0!0-""#&9 66>+0O&=8MN%Wd*AY&5M",E!2< 3@.5.;B71\ $9+0.?3-='*7,0!#-'$  *$!") )$    +  +  + + +      +   +   +  ;'+G@- .W "#=( 5J(:$-;0F9-0!A*5'$3H$/P>G"H%-'/!!))  (' )'$#    +         +  + + + +  +         + +  +            +  +  + #2      # #$!&!&&+./$/90VZ6dq+a^!I^"(HM3K7= 99, 1#' /+40Yd)=G82$(,3[h6Eb0QaJf}:N])TZFar@V}=Sn?=[65L82EJ:Q/!+1NvCXr%(3$ +$pqEhx8;Q9OvM\vCJ`=JU0Oa2R$-B#*8;Aa90?*7A!'5"&-"!*)%1&-8(,3#.4(18%67'4809@;2DB1KA/DG2SECT6=EA7SLPdQ^sOuXq{UkKJ]T@[QJfHYjJ]jRNkZYzFS\:IV5IY1MR"2.3/7?39BISSK[`YsO`rImwLZiC6<@3CFJWD8E4012<32K>7MH0UB+M7+GBGuYdwYdC>EQ8)&"%$/.&=@'A<6- -.,24=9C99%"  ! +   + +   !    "!# $   -(&(((!&!''",0- *7#&3 &)-".8'8 3 %4$;&/' 5(#J%!N496%9)'-%#    #   #!" +!%      +           !   #$% "$('1($!+#.-)(  &1,!#2(/%'2-+9-0039"'$# . #,)&-1$"6*2?373EC!.*)>-/3& #* ) -*,"% +$,/&.4A100+87%'%"#*!4(,7$'<1: 2<,C.? '5..# %&&("%).""73++#".  +  +  +  +    +  + +  +   + &O',((1 "4"$&# *$ #. ! "%11%$2!1:'1#     +   #   +  + +  +   +  +  + + + + + + +     +  +       + +    +  + + + + +   +    + ';-K9 0        %!#2#)%$#*1*-*:/707#',&)-+@>$CW1>V.9H70F5 80(<<*MC-JLQo;Ml6IeAHc=DU(HG&B:#JQ,FS/R^(WY?BFr9H[$MH"LZ$NV-.3:4E8DD6CJE>S:foCfI9a=_K`NLa@=Y@]k)_b)my+OT(!%"6B@$>4%- '*Id\Cv=2R2-)+#&# &"#,,<%);+= !/,3A*09&1%$1-.#,'+##%#!&%*")$"1(%6+$51+>+:@'9;+$.'%2-4G1BU'CP:ZtXz_rahW[}QJ]97IENXKNjOcvMYeUK\YMbE9RKH^Hri2ea(fd2TM>NYAZeOBYV=KX=Z]J_<7G07:@4B:67.:71GC5BC>AB=FD3?B?BAP|aZsQ=>(2)+!)09A!EK%CH)>D&DA#>A+.#)$,!1B@D>?=A<7   +         !        !"$'(%"#*"/)$#+/")$')))5#)+'(& (&40 ."$.!-&&)'0,"'/%" "'&&) ,&  + +  + +       + +  %" !  ) )""%!%!!#2C(4B)#3,.&( %(,+$2)0),#/4)-3&,C7'8,#$F&!**($-  $3!2!,)"(+. 332@&9"9,#)5!80#1D5B-;<7)58!(.5!!'$+!"%'% !#-'('             "/  + +)! #   (' #!" " +   +                   +   +  + + +      + + +           +      + +  + +   +2$=2D<_6   "# $!'!')%(*)#.'(0+*FH#3O.%?7-=4&>,"2!"+*!.%$+/.@IhEiy<^x:]9Uu<;8Mi4pQg2Q_'M^)Y8Y6g6RuR;[G)A:4F`U~OTv;kAkC`yIS|KCg.%5'+&4@ (%/@BMX>P]-[Z+QR3:A0;?+;E1?< .3*+")+6,6",%!+)%'(0"/D "8!'! !!$!'$,)-(.60BQ/0@ $"(:A5K[6Sa=;V\>[hCeY;ZQ6[JRq9?J5?QHB[[Wf]ShNIYH:UEFV;>->;45773::33534887>SGXa[U1L1#)#''0$6D,9M.AL*-9*1;';>>? 99#8:&9I)9G-9R/HW+ +    #" #" !#"&   &! !#"'05!)""*#%"$+&-%1$)& ,'."<5)7""+"&2">$":+)    %#$+%'   +    +     +     ! 0!-+/#'%&#$(&,+3,?6+B(7?4FB!-%%*)    &*$',!.,!.$$"*5.8%,9"*$#(!'/!4)($!%"23&&<#%)*##2##%%*1/4'-:$/&'$'$-',"$9/"%! "   !%&,    + +     + + +  +   +    +    !!   " !    !   +   + + +  + +     +   + + + + + +  + +    +         + + + + + + +  + + +        +.*,<"#"!   #."/0" $&"+7&- "23)*%%#26#,0-+"-$*''+10>Oq\~ER6x>YzMw{tqAoԘM|Y\j_^vE?_0XuAnBt@[nACV6[u3NiOmtWHX;9A.LJ`n2b;Mp8cL[pPBgL:FCA52G:,%36&'2>PI\xPWwOZoS@Aq}@PS>:G>+9<&51->@8VGfu;ov41@B;ONGZKEV5;>?;T<@NA2GPQoZoam{Y[lD]nAck2WS09?60=*-1+0=7:C7@M?VV0KF5jP+9.((%-&)7?C^whga6I2!!").)5=+4=$09#,5$7:(7F%:F%:B)2@%3>-6E2DW: +       &$!) !"!!''&!-. ,)/$   , #%#"+#*#&)#,&"#!&%''(#02""'%I?*?.%W&"B+2"*!)!'" #%    "&(     + +     +  + +  '$#$-$3*(")/( / *)"),,/8 *?+8260(@-6=)614?5#06B+*2#%$% &* +&!)*)'6&;,'52;C$3H#):)"$+*' -(  $ /+".((3*$(1*C1#$+&*7%2*'(" %&&&0%#.506,5 ' ! ""#!!'#   $$+% $!  #         '       "     %    + +   +    +     +   + + + + +   + +  +      + +   +    +  +  + + +  +  +  B= YO:#  +  + +70K;ESR>5"  +  !"%!'#.0"0&&&'3:$:L"=E35=Hiv_]:8;>71MJ=L9DRRzRWaW`JW`t[pBl}HGhD9A1DMR`X\{\i>3|0JZHZ5n'Ut36pI+NL+5=GPT-C;-.-131LU-MP%" +-7$>WDGi[=TZ=_M8P9K\9?S78O5?O(7?.9.7B(/9'.9#(0#*3(0$(%#" $ #&&!0?!7>#>@.5@2+=*0@37()!-;ZH{h}Qc5CR)74",2",0)@OH_|RXw>AM,BP9@F@2F48M1N[?AS--@=S&D@%4;7SH,:> 59*4F6@D'% #!!-8F60A5,>49P(?F.+:.1A,0%$&.)!-    "*!16(-!$(939T8GV4DK.FJ/7D1I^!LUNU8EQW>Uh@[C0K/K^$IQ#?D+2JB:_ZX~>]m7Tq;gw5@M13A5/B;2I.4D)DQJjVYVZm\n%8?*8@+2;*/<*,5+(8"17+5C9RkD   +    "$#-$# +$ %/).2"! '!-#'-#+  +  !  #'  '%) +* +%("&("2;#S!(a* q=)UI00:5##:*(%#  + "  $!#$#("'"$     +   +  +   +   +         "'/%65=6):$00$01!&(%)1.#,3")2'"!<0,4%-$$5*,.8!;I&/# )+)"($#$ ',#1'$9(03$%353<-;6!(#  /%6+  !#*-)* $1/$!;(<3!7, *! 4"(6".30.!# #& . *1$&!!"/ +#2 +% +&$)"   +  +   +   + + +    +       +      +         + +  + +    +         + +  +  +     + +        J8 tyG=&O6&Q.2.+:/,8-3!3 " '!"%#!"!&     + + &15)8-1"*5)MT$HX2; )4)$'#0*.AUiFMaB.HKAYV`wIu\ZUtwge^bzB?MtPiTo>C~]Drn=}`]`aFcwJ^<\jX_c;sb(i\:WcFg;;Z+,4!A>1\j20S.0@1BV1Sm&7K,Lb,JF&,6!$7)3@)4@-EX3GX5EU?CR.9K'>F.:D08D41:&1**# $!! ##" $!38$48)*&.&3>@UGIW:HP3GI/:F5JX@Yk6P_6?N<6F.)641A@U`/7I'/3%7D+JIFkIUuKZs?Qj+BK0LX-TV&>T/dvHzZgj`TU\bg\X]KdNDN>22013"<1#A4/QAKtXJiH.62*/)--8%;4"9:)AK,-6%+316@*=A)9<&++"!#!*736K+R!'W,f:-KC5!15##7")  +  *# 1'+6!:-)$%   +     +        +   +   +    !# $* 5/42!':++3!'4%2+&4-193<&%82'!+,7'8!(7'!-E.CW!PS',V =<GAN?P# ?$&%#!! "  )/#0! 3-@C&T4$1*$)A%( !$( %%.&2  %# -  " +,& !'$!   +   + +  +  + +  +  +  + + + +  +  +    +  + +   + +      +       +  + + + + + +  + +     +    + +       + + + + + + + + +  + 9]@EbtBYHANE^n>0V-Q2'#*'( ! '(   $-)#%+$.-.(! +%#-* " !* (1-8=@(UlBBX9&?$%3*'6IH1X7"+'=GOY;DK'+B:6"#&$*)6'%12,<7"5@%@A#9,+)05$;3*05'"!"  #*%!'%#")8)5=(7A***1,N55KE8ZAI^46E)5B$5;**5<0H;-;!$!"+1,=AKb(7D ")%-3+<#."#"$&"%* " %#!"%$/%)"(((,!3(/:/8"(02):8'2?-A7=A72<316C6K::L,BO*3?$(0, ,/(5 '*&'040<&0@7CWERmIbFUmB;\LQuKgap[TerREiVPzUOkNJn`wfxfwrQFB0fZ,XJ5J>AeW\`TpP->/!75";A5LJ-<8)>F2>@*/3((*!"!()'.('..2#49-4B5+>?3B'4K)37 %(-=,@R&ES2A\.()92(81"0#&.',-+ !-&    +   +    +         ' '+'# #*)5'4) (53/%:(%2%5%3/ )=)&/+#/+!,.+E7>()',-8*77&"%)740:959,/@(0C&+8)4+2 *       $$ !**(4)F15HEC- ,!+""       + + +!     + +  +  +  + + + + +    +    +  +      +  + + + + +  +     +  +  +     +     +  + + +    +   +      +   +    +   +      *1K9ybZGe3r!Ez Jehn($me`u8(##!$-'&&'-$'/, "("$$&-*B "2)2+AJ129#!*&%1<F:#\T>N_0IG*UV5lkXooqdiyOP[39;(;HVH;\jRt]Hi?CM3GeZQa_UfWC^BadQJ_@"?<&.=%57+",'"%0!!+"#    '!$!,114'3=,7<$2< /5%.6'07-5F7DT3]i3`f6S[4C[2@M$658?!8C-COCE6A!6O%BV,PZ9JfIOiHH\5GR6D[MHgR\~YnOZ|:I[9N^8Vm5czHeYhYlqwhee[ABN{g]z_FD;3+*$5>.RL2CI3CE+IG3><''%! "%)* 21#%)'7H3MU3>K5:D:  +  %  !    %-'! $      ?W%i+i8%[4.J8'L12M,)H8 791$&).<:4J99N<7ID>JB7J:?=17B)1G/:2*494A(Q(- *#!"  +     +  +     +      $ 1)(;$%#!"!&."8",+D-=T+F6&."/#- (2$4%* 0*,*"0 54!(+#)*&0"2%"3##-& &$$ ,"-5/   + + %%/&-K/K"<2,"4!") $%'?$A   # +         + + + +  +  + +   + + + +     +    +  +  +          + + +    + +           +   + + +        +        +   #'YYd eT!BM/PJ`fP;UDqlA7;e`}mac0'] ')3*#$$&""$(%#%-/%.(&!(":C56G6B37B:=T&0P4*HI@H<>Q3uBx|TLZkPsZ\VbbJv5,E,.C46@/%=3M$ .&3/%(F>6L;6I<,H\8>N80AK&5,>B#BOA:I<3J<5L9#B=)3*<8)0! &#!%'& " #!$$.*,=?,HT0GT-CJ*-3&/3!)8'6F,>H67LME][VxNi{8VW.FY-CX)@R+1C02A/=Q*=J,AP,HW4Ob5QgCZrC^v7Rh4N]Ke6La?Rb\V&G;*NK:mp>dj1HJ4>G236%A=56G=!02'./$ ',.AK>WCDZ3>V;       ! $"!    '+!%+ %!""        G\#k0l9'[:)H9);+)D+!D:-;99,/-8%6A I7 0<44(/76?4A?*9R)/<$%6",*!!  '"" $//$#*%*"=+""'$ % + +  B1* G1#,!(#+.(7 #$ ,) 22/        +      +            + + +  +                +  " !     +    +  +   + + +      +   + +  + +   + + +G(N+^3cA9VrFsZm &8HNL9XnhEDZ['F. B#$"%' $/. 4"("*(%+6:>M.IP-AK*9B%Ta*/?,03&76%ON0)?98_gReNCm;4R9;SFGSQ8M59<" )&":0-*&G=%5@XLEN'AR(0F0;K-DY97G2*802&#-'6P5Ec,2>*$)%+%"''+A%#' $#&- #,&!"   # ),(+"68"#,*0=839E7<2B3=D!8A'GN3[^8Ua2HL74@O.HW7TUPk5UW2AP3DT.=H(6A+4D.6L-L\2ET;A];@WAAYEOaBF`;CZ9BQ32D82GB8E?I^J\hEZfG]eKJbCD^>ASBJbNbbnfrdU]ry|qiK?:D|M8WU38<+>F0ac0fi5,HF(6E$>:'*0=2@>>N72D; +  +  + !)!"&&  ) '",&.&%0#-"%!  +  +    +  )"0 .$%2"FM(c.$n:(P8+72,2#'A#"?5.645).)"(!5!05 +2 "(7"06 7=$BL59Q3:J6(H=';2#7.)(-'4-2$$*&.#+"#" +    +    +    + +    +  #'2#5;0<%:8**A';#,6"3>#-D 1/)6418%6D$7E-1C(%4%*)"    + !%!&!"$&  &)    +! ' +  1$ +( #'* "!$&+"! 2)'#$                     +   + +    +    +  +  + + +   +      +    + + $$/ *$        +   + +  + + +   +  +   +  + + +   +  +  +A& >m{.874%3?!6;9Q9Ij9Lg4Be=zR-3SpSKViSNhE7`HD%1K$5O$$<))b+-K!(( ! ., +)" ! +-!+'",&$-0$0.,KS.:L09<2dk;Uk!(7-2)-;0# ',>XN>2G5&3&;:.6J5.8/ ,' $!/(?1Sp-/6/?!.#!#.'!(  +      $"$5'9E*CL%$&!+ +(1&202H3CRD>SMAVFXf0AE'/4.+:AO\Jhs3Zg7bl3IW(=M&;J&@O+;J7M`7]`3NN8AO<:H;6K6:I81D+16!".,1@3KWIToRauDTbCH]!0;&1@';?!=H$6H*2A&B)'%%'  +      +  ' +$ *1#-&,3)%9*   " '%!!)" ! $&*-+$ !)$4'(5$'#(+!!   + +     +        ! + & + +  + +    + +  +  +   + +  + + + + + +  +    +   + + +   +     $$ +  + + +          +  +  +      +  +  +     /?*j51/A$cV ds4X^A5>'AU82Sh+U2gP'sB(D5+>W,DQ+!")#$&!)&'4$'"%'/'*--*6 -,"(%/+3-<&/#;80RU/`n1PgLIc3@L"#0!)'3A*K'.;2$ 6+-E&CTEDDACH2;HYK[JLbC?_5&8*ED+,F=2R57VCNZ<:<<?K18 "    +  ((*&!&*-377I5CH30"% /71?X?Ur8qy/Z\,FQ/GO.4@'*6'A=YhIObL@TKA[BHeIGBHOyKn9Yl@W~]tuzwsg^csiZ\OQa]Hu5{y>xC@EwJ*>M*0N. :,(,*00'%4%.0   + + +  +-+0 +714DB!!$0(4.5%5#($  $ ))!   $(")'1$!!*&+-  %"&'!!!& ,!'! $  + + +    +   +           +    + +  +   +  + + +  +     + + +    + + #    + + + + + +   +      +    + + + + + + + + +             !"Q#W#E$+,0:5"3),3)):(/:*A*#2*+1)%&* ).! ")'%&%  -)0>%#;"2(-<>7K);!4'$/,%/<DXjFTdCM^:I]10B'  $8,#<5508B(Z]:#=;#E8)66*2@@E/M6AK(HN975KI BD-5,7?DOf2Z{4Hv1+7#"'-)[J>5QC      %"3,1C?B8A12E8%<6@G%DW@9)&=>(EL2=I>?RBDS??RQUnYE\AE`?Yg:N^>& 2*H''E/"G+$70!,'+&=,."!+90 $%54"2@8'>611%*",,!(/)+ 5(')(+ $1 $. 2 * +  +  +    +    +  +    #&!'),$)0 &1$,7%-E01C-7A44E/4?+"=+"'' ! , &%#  %% 05'.$457$&   +   ',')'1(% )%  %& 1/%#'#"#% .#)!, ("/!   "& +    +  +       +    +        + +  + + +  +   !   +  +!     +   +     !% " +            +  +     ( gX sTqf#-'0(K3>8;3%,C(!$%.1C" /*!)1 "$ $"%#1&3:%$3%$'/+2&(5,"5"#'$3-.2C6CYGMAef1L="$)).0-#@!159LbB 6,+*<>$=G''-$$-;=AY07K0-DI4HCJ#UZ1_k@0KK!(> )&&!!)7Cx<>`E3V*    !+ 62D/4=1:D,<;+0@@GT?H]&JR'@K:5E<-=6,7:7@62?@*9I"2<-?O6KG=M=IZ2GT/34<(BECV44:1 /<.C;G`2fk,CD5?JJ@eCIT.)3)!,.%11(977I@ZeE6=0*:21?M+.90C@+;C/+1D#:C#5 $"'%=?@FUTYZO'U1         + &! "&%(%%03D9Si/OW=UaLCUG-<<&74-+%**2=.^d6}n.zh&VC!/,8"50 4>1L<,B-*>#1)<,5@'&01I=ZA9P.7M3R[;HU(7;12 5;$?@)3994F9>J0/*;*@*)?$)7&*=$E&.1"  +   +  +  + + +     +  +         ""($*, 74/C&+?-"7,+4($)-%)&(-?!)1#*4,+73>%A*%"("8( LEDI$?)0$%;*$$+'(2    (&$+#6)2%!3$/0&   9&&:#$+5#4*   !        + + +       +  + +  +   +    +  +      +  + & + +    +   +  +     +   +  +  +   +          + + P#.*-F5{Tubxi[`cW:LHJf8[q-r$fn@vTS4h%1A,6020.+,4%-7 $3!%+!BB4>$/&!!%3#$/$EYMm~sMzHko`ixj%F54(15LC 2= *..pq*@YL[!225+EVL>U--7.DO'!2(&5895ss0O\0a)aS$*6(21FS!)!n~'7HdA4NG'%(         #@$:K+:Q?XH-:5!)-##!+*6MOlrfnd^q=AT"H>$6A+=G2NO)LB8/.3-7D+8!( &+7"7*0;1L=8S5L\)MZ%W_(OX0Vf6di#8D0@0%1/7+.N9 :Q&0@-C7'-K)#4.)#$)$$,%&    +                +    $)$" * ,*(-,+"'!!#$%*#*!)&/*0">5)*J#:1=C6F@=9N 9K5:H+7J/C)12&%!. %  +  #$*!# '$#&-!*& %!''!)'#((*" %#*     + +  +      +           +  + +   +    +  + +  +    +     + +  &#3#!'  '!*    + + + + +       + +       + +         , (9_=jQNpW\}ln:In7OYPV[st`un4a4>V+3A//@EGI0M?AB%8627%-#/!/- +/0,.=$%(, >=*/$"/+MvlJyXv`lOtla\Lk%1D-0A6.L=+0-.@35Mkbi:$34$&/D(!:B*A+/:.3>813(31DD*?]=I]GncJd=GVRBM9ep8Y_<_mMcyQlx@Qj+EQ&0>%-=/7,.""()4,&<3,B93LBGm@_p7SoDg~NsMxDp@kyaWMu\kT{_iq{kfjx_xWg^fZgURqYX~nqyvfv\wQjShQXs? + +    !  "  ""(&&,%)##&! %'&  "&"   $ . 3% ,,70/9*4,3348;F>?C%4Q+K"1A3!<+#A) 4.2&$0#*!!!AIKOR3@O13M&>D'M%*M2/+!",)""    +    +   +  +  +    +  +  +      %"' % % "/&.29.76*>-<-D!$=278"0G+?J3)H'H?.-C'!1%55(;K%L!%!#+*!2 *"  %(* #"$$*""&"!$%#*!+' ' '"&'*'&% %     + " + + + +   +          + + +   +      +   + +  +  +         2)*"' #$($ +  + +   + +        + +        + +  +     " !!"}\#S:[^~ec;zmE_vTe2=a7YrS`uK2:c #1I>-DL!-&6G,QZ0@K;5Q1=I'.9"#*00GF ,@ $5$:7$ 1C@ |,:#[Oz;Oq_RfL=WNaoMC[UX_[Pe>0;++ .&+"+'+)0!0MR`qiWk.Sj(qb%<0&*445E#=9 84*VO)$0=.3J&=72R+]R)Qb2EL9/757G.8A7(4!4%    "(024'./. %$((99&-43:"#$#()*01#[X6jw?{EEfg0DJ+V^&DH51$%$56*5>.;=2eYQjSWqSc|PdxJ`nL\nWndummdzanN]zPNjHN`= + + + +  +  &#$%$#!+"*!('     " !, / ( "* $)'#-(/209C7%=K!1I*,9(077CA:*@R*:B4C,'94&@)$41&2$!*") ) # !; KO#GY=0d>+A7(3/&<-8/*'#,1,1'|7`fw`.VODEV13?/,1/=E',3$2"-41".(($,0 /ODIdmtB:OiHNDLZ<H_TU^Cd{Abx8 .,")#) %*PpLDfC*I`WXNx(_r+4R:Xa2IQ4hDLY"]Q!)*".BAu'()%'  && $& 0*-PF TW-Xc+EB)91*[JMI!0584,(! !$$J? 9:%3=>@\cRpt_~u]yWzQDPACR@?H3:J*07%:K1Wd4bh=ZgGM^S;K?&):". (!*"' #,$+6(!/.0F/CO$:B',(-;6C4<$.3-GDRfIVkNRnNPmPC]D8T=?U12M;@]HXrRXr?N[13I&0E,KX:OgUf_gY`pF;Q6'419JFWjEeuRdrGUjAOkEUoSi[j|\VnVKnUMcKRjERe;O_6 +   # "    !% $%#!"$ )#  0 ":,"&()&- 20*!)0*,,/36);:%+G)1>4H5-DI)9I'BG(AG-H55E8%41+/('/&!* " ' !5NA)8]>7IH9@3&C$/!$$!9"'5A;@+7K+.I1='"    + +   +      + + +   +  +   +       !' #+'(;)<&'5"741'7B!->(+"/02-)40=+%@359.4I);J:jO:Us;Ne9#[$*$%).%&#./00'6%  + ' *"" ?(/G(*1#*7B+DC1I#5!$" (, &(!M0(O!)*(#%7C7 /O#">%%(#++' !!"  +  +       # +      +  + +    + +             +  +       + + +  + ++!1:02*    + +   + +        +          +%"$&1439#,1'8<%:9'%0&-!!'.*(-*-3//9+-=M/8IBPEbf[O{ezbnnC3@9:K%@7'%22,F$ $ *"%"#" /.)6!#;,,mTlgcY67!8>&0DHN:M\>CSFDU`opI,3"#*$9#/>7<@&,00!2[FS-RT+->\QQC6TF2D67HM7[E;W^UeNe7Af'7C(*-&/aD/,!"0!%<16 057%-      + .+TB71++ML7~QzFtKjt94P9Ֆ+,kq!OSaSVQ1x@up0MPN$V?5O?Z@CU:fOet'4G60D%#! %(-'*-33E+#6*)B%':(2%'5%(;#0''9/3@)3.0D+9G%4@(39 1#0B-':24H7P`+IU1FW@N_2);0."-2 =" ++#&,- ##"+!0%0-%"( 7( 4B$@.$30 3/+5' + +         + +  + + +         +  !!$"%%&*$$-(*4!)('4'3G4YVH_E %" ( $(-/); -99=$aLPfpgiugpqr#_siZ`|Z{iPD=X*4V0EE>&:ULT!F`C.\>6./%F$2("!0(%*4-70<" #"      +   +   + + +  +  + +   + + +  +  + +  +  +       +   + +    +     +  #!((&!"& &  $&.-&-< /7(;B$';  + + +  +   +      +  + +  +   ' $     8?!-YA?i,02!,-%('1 +$!(DF4;B 024B":@"53$C>%14')("));5LV+@XH!*))(%!C8:">M)5=,=H.D:5*4/;(P1_M35;BEG MP%0U66I5`X?b;UDYZL~I.cL>HEFg8JXR;U30X)7J+UL2cf3=s09EA0H@,D&(91!+#!5 :&!!!!*%"!!)"'+   ":(3 + +   + + + + + +  + + + + +  +  + + + +       + +  + + +   +  +  +        + "  #/-+',#)!#+#$  + + *""/5++2"'+'')' 2+$+&)   + + . +;9 95 D< @4 >A %0    $$$1 +   %'=:7AH0%S!##-)$&3;E3:G)?H*>B,8A"Z]1Xc'Q]CV1=&AM8NR4DS44H/=U9BK1;H(*A#(- &$84-KU>0M&36 7A#,6"%"**;6:''I !:6Cf6I$1FF($5-+) :DFKb^WoW0\J$DE?O#_`"X}%/ #4-$!39K'2<05$.(4-2CAA8/@:?L#*/>P%2O6:=CC?F*;G*4>CHVEx-JL%6G+. (  )36A +"$/1*JHJ\G1D@;ARws\]_^NSOa]8VVDUTYnmZXVWVji~CD~Iz{P{L2K7)?EKUJ_a-FE%>E1@P*;E2)A2-?%$( !*/5?SOUS7%mpWUA>@?8;(-)( )'0&6)2%((4#7I1BO9@O(+;#.@'7K&+=&5D6DYBUf7Vh7Vd/@G4;[Ej|PpKr?L[61B8;V@@XMGnTSk>HUKIgPmQhtKQc=J[7>V6AL73A:AU3 +    $'&1'/**2&*)'")"'!)%,6!%!$  ( -"0-*4#./#48&482,>1,3-5.(0/#:5$19!9"))"!>$+@5&A?!9B!&;".)6*! 2-!"#   / +1629&+1(   3",<!4-*%*+&#0$ (, + +$2+   +   +   +     +   + +  " "$#'&+'#2'+$9I=7HX/HN*CTBW3.TF/>FkD1w}'i,j~6h+Sx87cC5?-)5.7$":*,)$4?1:B&@C&AG:NQFQbBQb?+V?#3$.5'KE+eU.\e*]R&gi'bBcvK.[/D3&!="(((-%K(/C$!&0$HD:L*E)',7$/ !%)% !&+)))/'  +"  + + +     +  + + +      + +  +  + + +  + +  +   + + +   + %   #0 +  "$#$1+ $0,/5"  -1!-7'16)",13$1(/.2 ).'0  + ',-11/714A3CK6BI7C$ +   +H*vTr_2J4  +.*@P&^a7+H$"'&#$>?;<5L3BP/NL-?A-8P*YiHo}FSd>AU379':@/>A89L8:K17G/=S54A/&4!&  '#,&%15*2#4,%(,(2!L)B52S- ($"#/$4-#9!'65C(-@0<57EA?H;tS#CH"G:0ONB36?9$T4&#)"(.&--%**)/-&:L45a41)*5./5!65*99&1=18E4S]5>A*JK-\a3asTd}_Ok7i{,>J/;>?09%#"&*"7#2>&,=64:DH1GP-GN/2:5+E50>-8DM:]_B[HAIEL[N4RLmhlk\skX}lKur:d`BjnWG9B&4<5RiZaKiy5hg16 * &60,WfAXpbv[jNPdA=I5*@61E1,;32@.         (#$%  !"-"/3'5(*-)'/",* 40,@&7&%+%* +  & +'&2-32+5%)3$77&A<,6B05:)<5(5;*$>'*+!+<%JH >R.2J.!=12%4=& 0&"-$ -@0' 2 K1AQ"8M)=@*2A#:6=;A90?08&,8#1&"9! 3=&5"%*#(!%"*;     /1). +        .#,.*25:?+@!:26=,;33P1 UK^]4Ye@Ir97_F;EAS=.mc#H}D]^YOlKDYUKXNljBGs6/F%15Q6"A] N$3/;B%GL)bI+Vo/Av:5PBU=!aNYpZj2!#,,8'45(!& @&$/#';,#0##71+M-#        +    + + +   +    +   +   +   + + +  + + + + +   +  + +   + +- F+ ]` eeGh  + +   +( )# '*'3'".!( .(". #"0*$-7)3? /:'(8%%%!,)24"*7$(0  9=(0#.!39#;?-=E0*6;43" +    R(*h^YG'JHM;<+ +"4>,?N./LF.4/(# "-(@H5HP0DI0Ra5gpE^v35J0XRJesWY@J[5CA(E>,W\/hv4Zf02>/1C0(9'<4 %&-. +9267('%$(("R+d9 Z=),3+)-&"!%'0;''%*BN,G]*:I39O7dokj;@Z7WQ*^V&O.pR08$% !$($O5$/*:&-B2::1&4;=!+7+(;1@U'!$'#>/;H:FC]FBnWHlN8PK%7GNTPhl'>D&;K%!-$*%,(3 "") &%"7?+I^4@T9ISD=Y6>N&4B),9+&2.#5>.B:S^5'7F)49%4r$:m1Eq&HV%2X-E(>'J#:M:D,TRAOdU7O`LnclVZq39H(BM%>L&:B#GM(Q\8J^R=DT;9P0EU)Q]$CJ";G,BK+:M*8C#2= 16.9%8>+P\;nu"KE*0@#(6!/()<5:UBJfMGdFdo:F[AGe?es@O^EN`(9H$IO2M]7RcAOiF[pG`sYo]byAHb8V%3J045"*<)7'!3'(+"2"      ") 0 +% " .073(/21   +     + 0) (6!*"+%<2=CKD,UV/Sa'Bg1NQ1XX%Re(Ci5=RG0RN2GH4DE?F5bS.t@reOn=(mD5FA5H=EG+@ !0)23&4& . :!( 0'(8#4*         +      +  +   + + +  C$ @? #%  +  +   + + +   +H3OC$  +    6 DK;L+SOECZZS`RF    +-!+"$$&++0$$ "*0&.&( "/%$$%)31,.-,0$$ %#(*&4=*12#7@+A,*-(/368)@G6XL7YT(:2  +   !($/_15k+@dWfxFvafryJAw3gYc$@E.7I&G/$4R#*")?H5?JC5MWAOPB\/SI:woEGk|;{>dr<0L>BIRWiG3?(>H&6A#CH*(6$"91#7EAva *J1@1'Jdqu288QC;.0,GK2>WPIXShfMif9sfIBN0Rm":?09,8,)1<'0*:(*!%/+*,"(13.@!GJ+\T14<;@ c[4fn:jVj~46H4;K%PE(DOUvH|(7I"2=.;.8,1'/ )* JI &3"%5%1>05>6FC'3=/)%(!/!)0,6"4:07L07A5<%5E+3?*&1.))! "%)71ETAOaK_sEuu9ALVJ3MaCPk8GT#&)!%) (4,4=7(84'8=@g?OZ/*6@2WBh}8Ma3LW-=R54L2GV:?UFIaFE]PMlLOf8=O..C$!1(%;37J.-9&*%   + + + + + +  + &"!!#1$%3"-''$!('+3(2'$2$!-&#!  + . +*1-/ 6. .5$&.(,* +1!14,A4*4C$=3%6:'G5&MA'RM%QR*N]8RdD;_C>3>C&&"ES#048' $$8Z*3:':%,,4;:AD@AA;IA+>I02F>,79B3.7F74M:2E;6<71B,'=&,-@-!2J*;! 0       #"  . 5- (<=*[K1Rk6V^5Uf:]\-Yq'Ng3BI1D:4?:(MJ,SWCO[8CX@DQ4ZMC_f@Aq,DO=6F<180<=/;R3+<. -*+J4'?I21/">) "* '" #0(!        +   +     +    +   +D(SNCOV4;G%  +%!  +   + $601EH '   +    %0631KO[:H!'&&*#+&$( )9 &$  #1-1&(/,(3*(-%(!)* 4:0);'@3(6R%JS!OT%MC-vw?|IsVD#wQi$!#$. T,8?!/+ 4**=P[SGSctqUyQa\FAS7HT,3J4">*"!$,4@0-80DS1N[2+;+)3/52,@B4EWMZ]XZiMl~gf}MGW%,43,d7PrCFa */262/]o^TBd-gk;BM')<J3A JS.9H8)!'', %#&+,#1%'"&%''4$*6$,:)(2'7E(IT(;H#>N"47.7(0'% (! -/ 4B(CD-EpDlF(@++91':.2D,Rf:_qFhFnGlzJb~XdO_~0\f+js-{:{Dt6?P)FQ+;H7/G@F`JWuHaw6Sa,=K,@R,DW.[l,[`0=J=J;GU&=D%/$'.#0*'' $   + + +     !$(&&1!(!& $"#,'70/5 5$    # #.)'/1'5&$2'*/'@6$ME SN-JR/N31C6<0=69.I:,RU+SY9G^4LM&QP3L[,TR(@e "S,-*!(!.)*/$0"0-& )() ) % ('(!!0$43/, + + + "!!(&"/3@>EL"E%+80 CD34V?%GF/5=+>.8+ )4/#%2+%%# /3$D2.[R/VqFPoWM\M_R-Ma@U!AI1PQ1XdDXb2*:)30$-45 5*,)4.48!GN3Bb*?@:P)6,!27&-$&$&   + +    + + +   +     +          +  +" F=#U]CaaJly@[w(SXFH.L$4  +  +  + !JF5BT 9    + .' 27'8,9)5>(9H"?:! 1-83GC Vc.TZDJYB[A?1 E7#12"%372Y^;A&2)(##+),#"'!*1(0)8!),'#0(&6-)-16+-U]CFcP[nadsqUpq@ay:iE O(79205'0!,NBP`4V] %3 ",,b;6TcnWaWj?kg7?`.'>*'0+25 ;I+?E#7I62BB4>#79$0%))-3B6QUD[jEavXakK@R);<&/#50?ES06@/\c2TaHUjK:N$+/%'!!.0%C+#oI2dA3g;YjFOzG\C=A;E;,fy<1%WF@otVX^o@@N+/1*f~TKtNw^kkPخKAAc98GHS 3H)7=%2;'IT&#-!*!$#+&13DT6LdRNgMkT_iFIY7LZeH^NqM[tHUsCIXR8F>=M)9I&/?/0>0LW7BN-%90'=2#$  %$--2#-%2&,)$*(%.!&2!$,!%,!27,@Q.@O*7M).A%&5!$-$",+3.< &0(('*@0:#/"#1$#0%*636JC_|J^vUmV`J\tLgNnIkNhX_W_SeHo}1eu4?I!'246C@-EE4QGUu:JU1=M6HZ@LlC_z9Xb2[h.DF+)!&-C[{+WZ)'766N6JM!/7'8-&>6'@21J*1C+6J33G@HX7^h9?#)!"& % +"      $$%%%$$&1-+>6'"  +  )$1*%70%'&O+PHIU$FM0UQ6VX:O_MSSW[PKgID\S8S=7</%C5 +#'#*4*&6;..-.42& % 8$ %;#0&#%5)C>":K8UAGEZ?NPIF[9*UE,97540GH4_R9Tu<5Z@3F>SJICW1%Z<'(-*C5>_?5Hb60JW?.OW&=S-0;*N:6F('D*"0/'9&/&3!#+ ()$ ! $           + + +  +  + +  +  + +  +  *(*5AI=JH>OS6GXNTDIY;,D/!+2"  + +  + + +   '!05.CR8'1 .646+$/& 7##B9HP'KS/\f;GeO8WCWQVbn:nvEYo@js:Vy55(OBEhgCYp A*F3#D=!3:$),%*-"&2%!$&&, ,&$"+>%&6;*3:CGKFIP7LO:C!8"N1%L^-CK!A5`\0PIPb(@F:?Q_Qlg|3vh}gNukXVQVn6g\#>REC%LN,IP14B*2?08D2BM-4C*,9#.K@+X]@DVALOI~P3Zp(Q6 0^/(-"&$*2OAO#K9>.SR"-!#<*G9&zY>R?;VHMP=730O/'x6NhG79'7>G6Y6Z;KJF9-PI)9:ac?kHUxUYz.3>,7L43H&&,$/33CB!0/><*,7%%/*%"&,.8hM0@A@N;?jJI`KBW6;Q14H(XA+F<.I?CgMInUPtTRpURiLYkJ_yT|FXk74F%#1%/)+-/964M@Yj1DS6GVXq8[c#AE2- +   "19P@Qc"9@";F-8G"6:(1+!*$);%6H)?a3Xc!)XP1WeBHpJc_K\s4Lp@NeEN^E\cFTh7BW58J"FB"?>=DM5BT67JE,E&16%'7)#"2.75=A"2?>+;316*61(2136)7,%#%-&'5!12 ,*#-$')$,,2!%9%1"544=J;2W":8"-?&804&P62F[,1QDEC>GN88V<78&G9hQ)Zn9GnG;WG-2.@"*18E84DLGH[R'Ja84XC296AB(QQRR/Q\IK[D5P5=5G^P[]ZghRIqUxuMoz[VB\+dQPevTbw3RI3 %0DL2H`8QV4XY)N_ 1M(3!,0 '( >)*6.3$"/74$3;16<>%A+'<..8?>AFWHJKE6=3%'H)=@DOHNdAIh=8Y2`V^aIN~{w@M&,.(;D#0B26"#,,*.#'5#&'- ..!"&.'1 =K;DQ@GQ96M57H+)6#0<2&9/3G3/A*&7&.="NT*MW(5F!66 :L0+;"/3=$ "$',&*@L.?-#"%-#- ,$3 ":$- /;.8G-=E$)2&1$*'()%%",'@$0L,9$;H17K68G:,E.BW&4F'=M9YnD_s7JX&AK'$ + +  +  &'5&=L#CH$/&0$4)3M+AU+?M':E41E8=\2LZ&0;++,9!#0'"# "%!  +  !"!  #"#'''0%' /)$820",7%.9':6!9F<>0QL7Sa?VbEMc@EZGRT;I]BET@6K<@<3:;.=,,5*A,923QgO3BP4&.2%)* $(#3,)+'+(#0,.416$29,6C*FFBPXDBa<5\?3EFBDBBZ=?T@>V7FX=M\4LX-<`4=O:BD.2G#+E!%4#>0%EE';X/8LH2M>8J?>B-,@!(;#/.+3$(7(C;=H+*G1"8= ,1/&--)27+);%"8'&0032((3(-31&2 %*(0%60':2,%6-) 0'9C)?+%4E*6?G" H.%"#*'"">7#.<>+!@O1L]55S.E=*JW24RP6CFCD5>U-#6"3 N@V_6:Q;6G=!2()",         ()*#># -*++/.0(+2-1  %")JT"JG*OT6L\B4E3>LG,<57E@?BH'DQ(9K*!IY_`_jYXdITlFJPXJS\MOX8IU.1X#;$ !/%0,()/, 3@,1300:-TK$[dX]NV-T]-SZ2brDZe>W]2JJBH%`a:G\CNP5X\,4T686l^SGVwCreBwvx~txXU~DU2BS4GZ*1C%.'ED/_g>_kaFlG9_0-!N3GP(7*"'$4=%B7?L"B>&tqXvytffgr9i:)%O4.:>D&9>8E*ƝXZ(0K% *>=PQXuC5I%%-%+1*687NV4UL.y^81_o->&,1 &.%'<> .7 0!#!&& ,#0:%!0-*03'C(#-')6&.7BT&CL+:"(7 2;"E`(5L4CW/aa(T]!@L(DF>D?Q6=("$!$)&&-"(6%7B.ER'8@.0#3#)%!  $"-&'4%49(3E(&5(3&')!2-2F+J_5On2ReARoQ[LwHTgMXxT]@G_ :>$&#"&2=6XmONiBawCf|KmHiu6bq0U[@>;AA=;/$!7@17J/G\+AH'BP'FZ2[g%9B9C!3=-3@84E:3R5S_12G67L4?KAOI7B[26X=)G510#A;*N.#9L25+-72:56E+D89I>-?:*5#+/!;1,D #4'#+'")+#'$-/%,8(0$0('#4#&*&+, $5)&)&.0*;;/AE$B8!)I302#%#"&- F,@GEE04K)J;-DE JL4@R3%;*97)@<3dX,Ud 5[.4J;"BG-3/0=39;00:2B@,7G"3K/*52631>I45F=/A?":=,WQPY8SPM/B&E(('2*%QZGTaNRWKGTBDK+>H!%/0(,:!$.")62!;A/7@*88&MO97ZOJUNUdCYdLdjNo}QY|PZeLSm>,?03$JT.aZ7~2\_PbkD~xl`^w;l4y8GE>G!($$4.#!"%"!%*")1!1>67M/CQ:;.> 14)QQ+ji1amG=:4Qk)CM/8J'RS&&"AK,Ob+5G$5G&6B(" $#&(!,!7<3JY0Zh'SZ 2>&)&"""  !%!,"-5%/8$-5()69LP&YX4hx>Pm=h{9H\EMiNcNev=N_MSe?EO+5G!,5.4CB$SP5mUdXSF\{Ac{DRtCcNSrCSh9Nh2@K&8N+BT&6 ',7&6>3Oj:hz2]i8dx;aw-AH&L99OC?]H]x;Ql..++)& %$$  !'"  !#""*"(-)./#" +!%%'$&03.5:#1?&55)??&CE5EM0;O=@C=EG8NR0B@(BJ+IK4PU/KcGF[S62?($0&%+,!-;B,/BE'AN>6RG9F=8@34@3,9:18(,D'0:%-93%7#", %$#') . &))+%2 '(/-0578(8.! 01IB'AM/*G4%*!0*45&,@?<DF9JT.BW;,P((344--;J1,C1%: I+)XS",eN7K6)*7**33%2A-99'/@1.1/25%2&(-/+1"6--#$ "4%6D5?(:"#%     !#      +        + + + + + +     +    + +  (&1B@2&H M:;KL3FSF,S@.61*(13+?9UX>bGFl_-Zo%]d\^^_!Xg3AQ8,7A%/&(($ F/GP$#:+($7 BH+BlPYsCjt!b| ;J?1<5TN4fi:GxCnUbO^u=De"*C/^F>]gLS[H^R,]['K\L4MB0=;&K/!'"')!3;,<8/3*'3,/>:'eM2OX90@(":**97;2Q]QXoPFjQ(9U3KTMMDk_ApAps@]}8jbSFR=`g00B10*-J2A`h@bxB\wgmh\xp@sT?JM@I21A2?E2:M8>M>::%-2*)*3<@*FV64MbS=~KkZ04W03$:@Rjeczr<<}LڹTxgeh_j2>I4[h19Y0+2"&*-%4 +/*/!*/69;0;7M&1N*_h&8K#7C!ER-Sg@WnArwOZv[rVo3PP(KL*Z^(PO$M[0Pa/PT!5H2Je+CT,8!,%.4A!AV 5D+>*%9=8S@Pg.1D+$(    $"/%4%-=&9H%P\%cr/Pq?TrBlIx~FUv=ax;dsA`mNXn9HUBHf>ET(5@*KX*Ya5zBHkPE^:,E@QkLxLQaZYz=@O5BR4HT'6A4.C%%*"#/6 1>#6?10NCEjGQqFVtL\;fu4MU.0=(, #*&!79;O=T]5BV5<-2G86<78@0;K49B==C<8?301-W!T7&::7K.,B=3E.<:1'%$$ +! '"' #$%%0#(."$#"+%)+(".0&:9#BH2PQ0FR6,^JE:OCS<8S68C3:C,3?144 <$(  &!$7)5A+0CD4EB4H4*?/%:/92/0E%)<*5.' "#& +$(1 !#"%,* (5)..1:74C9=''@*$' ;$H@"IM38N37-M7/:.%6$ +"* "1* &N.CX-+DM2)(&1)(#:+ CB%8D)3D(5=+<> 7>#5B,"=*&%(0<%$=)&4$%3&3&;3: >0   +    +  + + +    +  + '% +,$ 0   +   +   + +  ,-/2=3>>>COD7J<2;$871=)MG'fY8ZkNOc3BhVI^[LcMObIhRGj[b[Ro/\*(E!+.#$2?8"(--=,"&#"!/$5>).<:8UJZBFZ+SZ4aX}}nno:UainaMkM`OEi.r,o:bKiLLeI8JSlIJKE>L?1[S#TLBC&-&.3AQIDHd>_W@^WEeg1BV5B9#Y:g!,DP_q%?;#Z`.HYH~k_q`q]QU8AZK00-=/N<1YWSoMoxY|`b~cJraAo7-@-,?)4>%1=2.9/*>-(5/09E(TQ"5U+>=1"B,'/(->CM15&VI!WZ=;I\,9<$(4#5K7N^M^pRZdxtzI_mTdDg=ZZ7K65I1CN:;K4BN(cwGbuYtevgZJq?o:cx8~?;f{7Ml//E24E0'7,$+"#%*$/,:'AM28J7OdDQa3[g:WmEiXui\Cky0io<[jjkXy>4~0R]0QX2Qc0gh%]]KN+@R3KV*AK6;G?CPA5PB>UFaASc/:?%!"&4*?DI];Pj:Sf5O[8>M6&") /'3 1 -!/05$1"6 2)#*#)$*"  !' $ $#!'&%!  &"'*. .!!     ##"&#!$#&##-+$*9.23077+5D21A43443!041+)(4%!+!+ !#!$)&, %(#)+!/%+%)3,'B<'=T28&)324 '(% 4$#.)'.G3CU;9N:GNc\\Ma?Q\XpidjvK-\#'=822<5!!0 *>!2<*NJ0Ke:MsH]n,FM.4(+'2%4H6=NCHaCZm:GU0>N0Q.Y/Z.a/X0W0V)M+U2[2 Z0^4Y0 N0O+N)?':$0"$!#"    !#$&      "#"& ""!" )"./0:,/5%-8((3'/0(,2')',!"#+81#$%+#$ "/&#/#*)+ -("+(-/,2#"/,!:*9D!BD9:J8.IB">55=&#,$2%!71,-+19)&6,.2.!9( .+*)*.*5$98,6A',=%0 #/$%2!+0+7/'7,  "&)#!'')%-0)91+#70)/3%/6"2:+&3#!%%" %')+"/#'"#0-3?HA$H;(4C<#85.(1  (1-5*,,./  +   +!!%!40+/EDH/A3(&4( 6)+=J8'M1*<2 8> ;C/1=!'.  + +       +' <74I0#1' (  +  + 4 DU*6@--<0461%<.N4$jlGeqVanY@`Y?D\CRU%B,!%$/-+(7%/dfRDb^hK4cH\T^ymNpvvf}hopZVi]DyLbKX&6X6?F\Q6dtfoaaogD_]QihX\?Yp?:uaf\Ytymcvz^_q}`fKUU7CC,YH!,.$E7/FiBHT@u{DQsSieO6*.C*"*76>)/>21<%6A#)8%078P-5HR03_/*=(8;.:GO[!ae6fw::[:BY3A?NdN}wuv옪qšXWxtcB{AeIdvLES]/A136+6>:+3;6<115<>K:FX)]`+z& DD,ef(DJ(K<51 5031/=44>;12B..3$8;EL*>K,VO4CE6bc3WX'/8$- (,*EG%37();+8D.0L>q>tC`bl^bUcWdMbWeXoOpZsSxNJUNb~_a3IW""+&*,6,1'(+-MO,Ti2I^0(;")+CBa_&8\ (<%,(1$+%+"   ""%4IaP_yUZtOVxHYzGRmEJkDe|IRiRe{bqSoB[sCIcNes\`bntou~|su|snHq^KkWJjWOyPVzSkbhbOiQGgDNe42C23HDYv+IP#BI%9M7RjP]z?]r2_h&N[$MQ+>L;BY?JcHQ`BGY8G]7K.G4K.P3T8K3H6N3K0J0J1K2M4J9Q3M3K2K,H/@(};'v7g3P/=.+(($& "! "     + + + +  #") )(" '#&*+5()/%%2$#,$'-!))-*(198 !    !&"(!!  08.39%,7$)5"%-$-((,1'5!.0<,':(/- 5*.2!-6()$&"$ /. #:&.!+'!$&%%( '/$-)2&0;,7)&" %##-./<,;($4%+#!!(%%-&"(!H*CP%5D.066@(+;+66'1=#/2#2=!7"!##+#! 0$*5$2"@-'K2($6$!"  +  + +   &   +    !&1)0$=) 1,((-, C8%2C91/.()  +  + +   $#/90'1+")A%BG H>A@$#-" +   5*3*<-'2--+(4/@'$QlTOm^Pd\[iKem:ep>hv6N^%/*%,.0@APSY_40`I7IF1G"3U(?D'0C2$?,-%M@!onn^BH!*80/7,'4(BG).P/:)-2!M>)|Q{2bs8HbZcl_ksmbaue?[NHKNN[U/T!(/)0;?COQlOglO_AHW2eP O@40*&4*)/&,5&,92DTKSXOkx\sdzJMUMOSuRyQlUZu}nz_H6HJU^$WoKRJ\/_{BOeC:Y8Ja_\zu-[}1&@/ %+)9#1#1>09#&$,!$'5>?Y=A]B_IwDiy?guPUpL_z\nYtQTThuch|{rwkJq[,NY@cZOa]P]B9@21>32E:BVGMnO`r^[vUTdAJXDH[5R`'AD65G;Sa/@Q.AT9:T?AZ@Bb=Ro8Ue7Lg8N\6:IB=QR@XCAX?EQDl=FxCIz@D}EB|EKEKFJJDDIDIFHAQBKGLKNMQJJNMHMMQMHGCK@I=C9{A6j>/d6%K6"B0#*,&%!      + +  )&+% "$ !)$"$!$'#+ $%&#50$7" 6"83&0  !""%.#*!&"#%! #-+-6'35/.5'.8!):791=/8$22)7!/*$"## #'# &#6",A*2%"""$'-"$%&$2.#  ."+7%"6,+,&&%! %" & $)! ')6D:AJ54I=3>35E+2>958E># H&0&)-)2+*#") "&'*%/ +#6*1*$ +  +      */)& .       '%5Q7KZ(F\?*L+)   + +   *#2.(IA$OU$Qa;YZC^]HM^2!7!'   +  .1#-C*&4)$6"#.**:$<92>EN:;hH>aSBVV>fV WNJ@5:Q&+-)7/$'BUG39376"9E&&:!U@6CV&(7%4(&VnHjhTx\0V!'9;0&=7#6'79>N*32-X.EjR_VQzAC]K^nobzx^`X~CJX7nYE>W2:?''5%,4-0BRZWSz7Sta/HQMt59Y9*GDF*MT%,.&6D2:>+2=pF*aK;>;5AE-;F.DD);095)53:E$06"&6>4gs8~LmTk^]fjnc~RwLt[fn~ܧꇧl`~owm_hmSu8qGzCMGG"3D":B%8D$7K$J:(   +!:=,30 "% +#J<BU,%RGA7HMUB3OIU\K:P7?>7B- +   ('*+%'%",#";2!,2(*.-& -:5!&%#71E%-0A15Ai0:;AM4*H)2;+;IF@#$%A1a3O',"%+=$(&/35UW9}4n|VvM_5CQ@5D.,:rmPyiqgn{xs\J>T6-B5!,""4/11D@*LH598!+; (5$$(73)HJ-L213&,A'&2A(:;5C// +&/6&|Z&eh_fdrYmGeveZr\pjS}`Xwsظ΅Gnw1J^}MazOVrgDRGMDBNGJ5>N=DWEWYRh4GVdOtyyc=p;X`>UsNo<-R/ 3(&!'&"%,)D:&89 :7%,-#FE%B0?OK1UC.B;)=6$C;#^DQGO?^K0Pn/+<445?.PjP[OfDFbKmamlqhjctVT}??gC0MAMf[zt}\_IXRJ^lKj{L^zZ_Sm~>R_)=T&@eGAVg((0#!+$+/.)((<$#, +!#  +*400N<5LD;ZH5RE-D>);=7C73I;ASW_uZZudogabFZtFdF]wVRzIIf1D[0Na-GO*0AQM`SizJKePpDs}7Uf?fu?L_Ka{cjcl`aRg=z=q:u=s}@:Q\=Kb@U_;7F96V>EX@H^3BJ66C@M^J`qI*$*,!/ - -#'$-$/$+&0"/"1$0)2(4'2)1+;&8+B*B!(A%/B!1?".A!.C*E#1;!.-#.&#)'+!&!!! #$$"!& ') 0",#)   # !!$&#,%+-, ($('(/!+& " &-%65(I 3*.)66+:#-6!:7/F!$5")-!'0#).,/!3< .>"#:)#,(*#- !)#-$,3%.*"-$ (4&8$ 5-), "'((*#$  +         #/&*6-  +  &+%5-/<-0/70-" 7%+(#&'#$ -28;.:C:@I7#KO#%3.$*) *'&) (   +  +%7!(   +$+ %+#2 " '%>)P=GG!F29(&"  +   + ?LB DS;L(DQ7MK37N&'5#& !5/#3);'$:AH=BX7YD7N_,&7 +6SLR['Fb 1'*"63 (7$&!!"" -4?2*.@'PP@CV+>L9/B*-0!2A'&R0  %(M)%T0, $!&,,BV+HO8EK6F7.83$:+28=B)6P64UM>G91@-<;!49='A&%' ""')8itaDSEia5>L}8]d]E\ 7>&DFEbk^=TR8EXa~Wn^ps{m~p}hEj@>Zmi}lntg_U[8AR'11""7-A"HA,>Q6=@.,@);G%LO41],1N40 *,##1"',"'#"(-@K%GV,bd'8Q(+=#.7 2=14A*?B$JR'6:VA6_zifhyldWkRxWInH]lPfiku}UrYŐSgv,=*-; 8A#J\'E\4N[?]H3Xpl_nT|KwJg.6D"(?),F<``T{bKgSm>NaJfUEUpQtm^Kp7Da)!432OELl-LO:A%,&-))1$ * #  +=BCK5? "2)**,+.'.!"1#+;&1<-Kc;lH\r@FbYkkVkUtKlFr;Zg1Nc4Vl8JT(CKAQdXkLWoOgazQ{NsIrMcXd^l`ZQ|MQvVwOsQ_|DDT8APHH]?V_)@P7KcE[o?Wi9G^DNp\b~V!        +  )0!,'.',$. //.!,+!(&*#()('"''##&& #%! ') /&##  %"!!&&)/)5/'$$# ( ,('# ")%!:*7+,2#73>?*H),:0)8!':0$1(('!,,,5!182; "@")3**70$ "&$*!/0")6$0$!* ()2&!&   " -#(4',!*+''    +       *!&,$."  !5,"7;$ZB'*C(-# &%#"$.(',>.#<5" #"".&  +0 99+5,'(.."#", %/!%  +   P, TPMIL7KO!>M8/@G!.9+ ;* $) )$    +! +-;#87.9?1?C!,3G09CPC;D2-8!#  + +, ;%'_Y4\c?Y]XP[=JI=EVX_f:hoOTmPBR12.&3-# "1  %$#;? 3.H8/]UFCW.3A" 5 -2",( + !%<$)-'6 !-%$%$-$!1#&D5CN)H?0HS2HJ2WS&}m5~!Eg2BC! W1N;Q8MRECp;0;(3HSL`75>0==-@NH7T0>E!_jAM\H~Mm`Yd_h}ad{tU,;837?e_TkyaC?V*7I!":IH!M[6@U5.D+/;!=')8G-1F,(+$  "*0*058'9 !;/.z2T3US-Npv[Pơ~rN6Ji=btFWp_N[^r>nP^}V:M[6VieWFxxR~LvB]Qt~TzyI~'u?zyӆxzMq^*7-7; (0)PGEC.8&KA9GNdiUPihPmgO`u(`S$5>!D6%NB"C@&->54%B@"GL/Nd5Gb)Sf1ayLWhncCYsH{ZPUl3ZfEJ&//K]NFZ//FE\Ur=\~hpa_K\omcDh<'7 '&)-B8K\2Qc2=19L(az;Yv.Mo'AP )'1*2)2.6 ($1%3JBQrRUu=UhMZwuKgN9Y_d^uQoNm.BZ;SpCm4mvB]tXasVcNcfl\tOHv[kVnOvUq]tfc{=?ORS{b{SnBbo>EJ9=P?Pm8=17G&&7&&*$*)" K-PH"C  +  ,2N@;RS=MN30M  /;1.5 01*1)%&027;@=E69A-:520%  ;(NU?DgQX_UbkJUbC^o9Vd=cjRau\BbV+2?'A4"/  !!%!*"+*-2!/&%.<-9<BQAHK?%I+$-%#A, + 2# ++(3' .!'!  +*$&$+,%+-IL?\U6=EEAE]Ael6aP.KE#9# -6R/JF8G`;WP?B#KP@1P+95+dc/4V)(,))4DD3ImfjegiPcX[xai,G@%Yb>HDAGRr@QBJQ':;8K):@1HI+2@"0=1,79$$6$ -$0,0=k.*A%;3HH)5@F16&gfJwUetpl9RY5Xe4xr;{lQnTuSvji^:P[3LbqDeF^~jCMGlxMet`|Om}*y{&F!8+'+ASHiGX\[PLoFE\8=ZTbmhQA] '.&(/+G^,Ja*CM=M hr"^g!cfTdV`CS:?!,3',297HDY'l{>e~EK\21K4(;& )'2D 4F#,= +1"$-86!=@Jn=7KH-B3&:?9_ZZwT_pQX|=]u7D^CBeIlF[uKYqPYqMIlRX_dHZiCNlMWoQ[~OVyU[VQu_\z8Sc7Uhc]rSUwHOX.?KFRgAMQ*/:-,/5!,7"7@'7F(9:" "        +  + +7055)4%/)%3*2, &()'&"!#(%%&"!'" )(!"%#%)!+#&(!+ #'#"!!'%&",&+).#,%-8-A,B)70'0'$,#/0")#"*"$!!$ %("'')"-0*8 .74:!?D":P*0H/6?&/C#A)!6,"2*!% ((& & )%/,!!) , )!)$  *" 71 %<3!$$"% & (#%+!"     + " ## "%! $* / ,(+(#2$+$ )" &!1-5>6#6!.451!3 !0! % 6-=@ 0J$EA69Y4FF*4K8?E26C"!EZ6H,1@7.MH@V[>RgLMRVQfQK_L5cQ;QWAH(&*! ( "!&))+;5'E(%"(##-&^pGnn7ecBg^)fm*AjKN\1TBOqQChNR[JZb>bV6patTuN`VeNQQZSIRL,O?@SEEOnJTT;\YEhj>Q[4GP,DK*DC34?+3<#ET=t|;_v=DW29E-9G*"'*7 +G-B/RgL5Z9#*+5/CURVoL6;#&-$.18 CQ8g|FYn5Ml57@+?R75IH6KJ8Q(3@?SN<,;B(38,69+.3*#          + +,()/"")&/'#3++1!$,"$)$)%%',**'%%( %' %$ !#")$)!#$#&"& *& %$((+0+/(2%1)/--<& B)#.+)0" 0"3!5$*%* !(  "#!$+%044:;<*H&-@+7:&3E,*?8&:767*'F&&891/#'@(2 5$.#/(,%3&0!3*+5+.*1)8%/"-*,(9#51&)(+ &)&"#" '%0+75(9"' % . '##&&#($! +##*-,"+,!+# ) # !0!.728!:B"99)31*+1'&04.;#0.%8449@5K $F44,65N.B@@2Q3/5;.;0;82EB,$? "% &A&+)D8(,1 '2)))2(5*0!%,58)/ ),3396IB8B+<""& B3HJ$AV==T:7F2@C58G..27;+A)&/,&/'59,-?',-/3"(7!32AQ8=GGBAU{cU]PSRYTgxVouJaa;Qe+ii1Qf2^Z2@U;Q]OU_ajg~khD]slSVS"IX1+:&+. <4 (,5R4-C6"3+I/"̘Yćfb|X{X\K]p[|n~_s=HnepbRi[U]s~Z׈ޡy]dlJ_:\nTvY[zF{f]VUpi5_SHhh2H/3L0MisJfd4nuҐƀ`PDlGKa>jh.7@8DG2oo;ZkHVfVdud͢ZڝgqWPV?F@b5=9)!8C;,E!.5+A #/92-!#.77<ST,{>lHmA~8.q{&?I+hm2Xf"ce"jv(_p3]k%>N)?O*I_9Ok6HfDEdFJi=9M+2D=C`FLrESr/;W3Xi3h~9f}:Y|YfhvlaVE2fr"3& %-*")!"'#"//(>,BL&,3D,SkPtUMjSt:ag2Yl>[hDPpGB\@Oa9IY6w1U^(2@32E00;/#2&! #   +      ! $ +)&%&& ("(%(( #)&&(&'%#/')-,$'$((%&!+&)(($*","/%*)#"% *"'"+#)+-++-0. 3381A$"?*2)6#2#6 <%+'!$()$(3,6:->.>$)<"*5)&?,.4+%:!'6* 3,+"&)()$+&2 )+!(.**(*'(-,)6#5%,"/**413>#3>,>#'0$/$."$)'+'+ . &+$('#$$&!!$&(%1)3;0<,.1%*.."2014%9 !" &  + # $($%&$/&8*#3D*6<'=@':I!2L&*9+,9 06'229B(8H,'<.1?!8A98G2.D6I8%!I%#**1,,+87,3D1&=(.5)-!*3<-,@,*4$'2"3,(")+*8*0D-A%37/065+:;=6(8L&27 /#/9"=?78E8)?6.;(4@*/@4-.&'9&+%!,"%" -)/6A+4F(E;6A!A1(3C384.4B@,A8201**:>1-8:580=C7AK>0F3*#"0*0#%308@>'L133"...9/9*;S8&S*D7-858<)PK/8K")2 2 sOrmZvl;cC[8BlBTY[goJ>PBYtRzJnyM}y:evAmq^n)]ome(-)"IC)DN8?>/Jg90>'3@8T*2F6AYBhp=Wc8Zz?v=_u7Ne?WpJQjUTqcPxlyg+,;%3>$&?"($%#'*$!#".&33#.9'%5)$,#)%#4 !$!(( ! !  J +;0&!  )&2"",*):'67,1@,#7-/)),59#)J).?29A3+D.-4$-2'&2),02*E(-;@$.!'#.%!0#13-4J;0A(7C-.7+55#+>8!4%)"&!( 8;!6L,4C2,:--/)?3*8O1AE.DM>VQ+?U*.G#/*))5:886!#%&'!%)%$)($+/$4:+2D9<=D95E--B9=O=9A-@D7@N*,E)+-!*0>69C/8?,)E)*4,.028;/@ )90C(7''&6+$$+&#-=#0-&-<'),)36+ :$$&-/"3HL,*/.44#FRH)?*   KY=d{DWc8_a08U69"'@").9<5v"!%)-0W'3F+74)95A/3LU>C4Gn=WmCXh:ZhADMekasgcnW`aV>jBKcpNmk/F,7E$rXxi:TeL>),%-$N8O9)tIxds>z|4xxIu=C{SoT|rqPW~JYpeJaFpk1}QP=dGH[_`q}[bn^e\}tS0R>3=4vٯȎnjlOdYqZËdOPdNkh>(( j*PJ $5""5+):F&/N>DQkyV>W`mp:MI"**%-.%++*5;225!)2"/3")/#58(ED-0=5<&3"(87,=?=MQLeIEa2=@%>L4,1#&.#*)5#,+'*&9K!('u~Mi[\l`kdroTPQnLw9rNoZpj4Oc94FDOID~?v1OX75H":J(8H 6?"4598-;$59!9=%6K)'44*):,+0'3&=02D"+7,&+. !  !2*,0-/#& %#1;((0&/*$"$)$"$%$!!$#!*,!$!!*41%)G- ?+)/9!8%"*!0%(+&&(5!#1"$ "!'!!)$/')/'4%+1D:&E+3+(#+#)$( &4-7=6:7>3?B*)>#$/'*#$4#"/;$2HN5>PG&F7)*-%"0!%+")#+,(/B/?%%7'3,%2=1!8(%&-@3.OOFY,/E$.8,H73DX8AQ!BS&:M0;A8&9,$1& #+-"0%! "'''&*3!3,!-:)>9$/D' ++-#7F2!*.,-(+/7:3;,8&,<+5$$$#54!=:PA;DK(==#>V$MQ?-g5* 60 )2D8^Ul}HoE"C)#,'&PR1C.22!)) "2,#.(/'GD*>T'1A844?=G@DR8=J)CGNGFIujfZ5]l.NL8H.,'OqԎKI^`?NEzv:7N$.Os*Hs*]qwV_xutk~RssUpvxv|upjSWa6Xkg@WZ2mq@xKio[hydLj^MjMYmBLZ1IO0AVazmZggzIоPv|uzvjzH1GE"34XD&6?)TN"gm!HH'3'3,7(>N=l:]b1\_('9.!'!%#&##+13sZDX@_jJe{O9HfTnLej_?{QuÂ偿looUP6~'`h)JXA_xTr6YyV\}UpK=m|2Zn4Yj8>W27M;)GOAiWb .8%%+"$*&-040;*7G&8D",4&,!1!!.5':,*9"#,&.996G64SAKgSRd\CihGoieN`u7HW@WbQpnn`eL?c]FI]ALcL`~B^oBVl6Nf6Qd/;I(?G08.:S=#%!-%%)!)!  +     +   +%#(!+"&"&%$#)'&&' .!#$ ##$#$$#! "$$""% . '*%)%'! '&&##,"%*#*+%++-0-0#;2#@6$h3)W@$W<)F6%@-!7(0'#*#'# %$ (/*0/%-0!1-#42',3,/,+--%0#.,/"/4$*6)32%2:"*7$'3#,-%8.4;/; 33 19"37'/8"16(23!48%3493%-7(01.1;9(7C)*;-@1 59#72#76$@6)78%:82@/8 0275:7"4E/.@-*0&/+/*4-7/1;+3#H0PP4&D6)+ 3< :D+5@$+= 79$\Y+XW"=?2E`6-N=IVFVm4Z_'2)$-1JdYea(F,#$+'+ *)VOgU+-#"'.38U6av_tkj]oMgROTt=E[7JTNqcySe{Txjx{x{fzLGiUg|h_H;qO9dx@eJoXUJh>B\3,@') **&6<+@1'6&)!!)2".;%4>&.%%/&&"*!&- + *&0#,$102O;A`:=V>AXOLsRo=Yo=>\RRx~|l|PNbDOaAVgGGgPZr=SmEQj9Ro1DX-@X+GZ1DaA%&#'",!.#&! +  +   + ! " " *'&" %&$#$"'*#"#"%&%(#$""$"!" !&"%*#'"'!(%%%((#$)$)!)'-*(/!).#B/%^1#c9 LB'S6'A3$A(#8).&!+!) !() !$*$*+ )'#($!,#*'2)"6*0.1+)(&)!.)#/+ 1.#!0&0'#,,7/5>*:'+8$,2!-0 2-"3/$00+5 .-!,230#6-0927#-<19=.?;9@",8'04%4/.-#-0%4!$6"04-6*4&0)'5/$47!1-713;96":,'/.?+17$53 93390<0:&(2%','&' /(+3'/#)242 .//(0/4:.:531:::=E*=$214?B!EC2"$$  )#.'/": &.$+1.80C+1@--C)49+1?*>(G6T\*>c-3FE=9Q#4@!AQ2=L(EI"CP4C+@#+5 8/-;?%:!,4*,-5:9B>?+K-$1$#)-!#$ 5,A?HS'BL6/O-++07110(*2!*4>%5A()8*@2BN).?!(8+&3&"%% / 6=6>!-A&:&$$  ( 3,;40>+49!'3'!!%&*&'!$&%'0#4!#' .149<@&[S&Ib?HMHXVSlvNSj5GW3.S5^JLa5<%0@)07*J;$Vq<y^]uTjq‘|i,Y""+%%%#&&%0/"c8rukgwXxg:CaJpL\Mz`Pmo_mFB%G]gW)nw$dg?ôqݗe}ĕt`ccU}^i}LdPMVcNzlVrmPhZcXu9`ІÃecqtRhnvFqtpӝH6H"-13J(+->8I-9 -;02=4-C&*1"N]-1:(:I#'&"(/ )4-{|D5y|48.3+HC,Fd:JX/(A)(""04.@ADb2'2'3 *..4jkE0I"& 0.4777*JLaiZ=Y(MQrj)?`RzIt~T`yX`PSoM]S~FFDNmvLw|Il~wPxZG^Qda|ASi0JG8GH=]BTlDay@`i:ds4hyZlH&#&##$"%%$$%'    +! #  !$(# ! "$$!%!!#(0***#*$()$)+'(+&1'+'*#+#- &&$*'2'62S-!a6 G:08/.("7,#<,8&=/(+!)"**!%%$&'&-(-(*)**0(0+ +)#.&",)1*+)&$"#!#%&'%-(1.$15(-,(/.0*/)5+"-1!-+ 2+5/5,!22/3 0,&3("-+%+(".(**,*,,$4&4+0,#).(+2(28+#:741!/(%5**.&. /*!7*$0+#-)1( 8.!24"4/!4*#*2 6%<-2+2./-.*+0,03-*5+0-23-"251=+=%98-5D-28120?):886F36J IC$@Y:C? 0&-2?'?@+,>'),)/ 07%.<#<5&+C+)7*;;#9D .S2(@?4@)2H6'HA47<:?0)C25<1+J5+;66@.;E'4K'2C=0A>)?#928P-<>>/E&'(%!+ 0/E>HP(4I9)8<:<->I'4@.4'3*08(6$*2)79(@A+7D8/;6 ,#"!   + ("&0,&)*%/$).'1%""+$K6LU[[2IT-1@.#.%+.6,"/"#' &"0)#)$*"07" $0,-+,-4<=gZM;`B8N75T[XBCCb>EF09B0LDW<^.3:'DW+S?,KiQzbh|juKif1Ufbb?q]uf?V5:"!)8)iX'}U}s@IK}D4?'MB(ch*pGQd\@WRt@]kD|yepj}pqLRok{cgoWr[OqHXnȅLJ}q_^?UE?PIve`ϕrM&8)ugVlYqxVW䞎͉vsbS^HVWldu^jyp_\nt;z༽նuSiJN['5<5N*/44#"&!+02:,8N+L^"%US%2C29 -1(EGm_&LE$dT€ÃiS.,7>=A-5).*1B'*./1$/?N+OXL{&HP!@Fcd82!-.(VW/>KE8OQLaMWJ\mhpZ\uKM{]gxM`vI~E|?X]sfUHXtTBQA6H*-:2>VIhI\~.E^Fi>jHo[i_@bRMqRMmSm^rbzgeKhw:?+/%CS>I$GV+Sh&M[6A%-$-,76?&+?!/;/u"JNEN$$)!/!/, !1&-<#0?'CR5McB^tIv[kdmVg{JOfADW=EL79=/8=),2/(72/@EKiPf~I`tFXsOPlBH_CAZG*-.&.($'(%$&$  ("  + ) % +&) & $'& %"!!% /$0)*4-11.3403"04-5*2!%3(,),$,''((+4#F*\/Q?:8*,4')* ,/ )!!5!17))#.  "$1!2(0'%"&$"-&.+.*)++(''!-(.'),0%**+%2(+%% &,+&"."$1%*%,%&'*)*% /(6+/01,2+ *&,&5)7)60 8+;0 @-8480 00"0&2)(*(#-,31&54$6+.4-'/(0/1( .)4"4,0*!5(3+60<+5.1**%+")'+! .(0+2+7*<-7689 5; 15!/5"39'48"<7%?H%&.502%/584;/28#4896AT)8P,/=0=C"3D&8,.(4>?)5S,8B+'?55&"+ )$(.#!9G(A50:,:7&HE$AY#/I$+F5-:2':2(2)&5#%-3/-.>5(@?'78+.%)'! +),&19-3>-3M?&/1(1#8-AB'@C-:F!8801"'%-$/='48&2;6-63D>TI+=938$&3&,&!?A-=?GK.D'4,)7&2/-!,# 4;/;K-5R-3>4,DaM'C&&&.,/*C?0:&9F:UR4=_H1GN4Mp%>k&4'@12AKp~ePkSiqh{1iw!0.)!D1m-G`ph`PzwQam9^D\n=v)GTkmMЈwo`X{o}eKfuZPD7m|uzyuyQ28zir^w?j_kπqs4fw/NtIn[ADNbzŵkĥǐOx~4E_CFC5D*3>IV*CU"+-:398,=+3'HH(7?$-F5-1%=E'2>&"-:}ig5}zЗŌ{ENM-).C1EIG[N^vOFeGHrLiJ`vUvkhefysR+GT%5B ),),/H\F2\{:Hb>7R=9YC-F66O05L.4JA8UN7XVJobOwaddZz:9U+8L#5E).!+C8K^,7S=Dc4:  1 7!* - + (# #%!*!+ *"2$0#,&&"(!  $#'(%+',(,..-,4/109!35$5=!9@!:@)7C$->%-6 -5/0'1+5&- *!)I#?:7C%-9.%.&-#& $3(.-! * & %((!5&<%G/K4.8#,/+0 )( -,0.*2 /&0.-.*,1+02,0 20-+1+-*&'*&(&$(*(0(/) )',)1%(&*$)* *+"1).20+4,1*1,#+)#2.!*%#-#$3&#-)$5'$6'3++$,'$!")&!0".,$., /'/)!-+0+9*:/!,*!2)-'3$).(($)' 6)2/*++&=).66-7,6/"52$+3#4,042160/5/5"34)92);:(?8<4B4O< ?D$8;6D<'(?#!.1* @KDW:W29OBEH',? <35>@HLZ/O#7>')7(2*05).7 &= 56&/<38741><,0.+8('.!/( 03-39*/?,*0+$0#$%  & RDEZ!DM0BP3CG;JJ1GX5>U9(%"/+!+/%' -0,+6).;&)7"/0B5"L'BO+>G(+5'/2-6<;==71>' 1"#"!5F)Pd2[hE{F_sNKZN9N?CM>M_BUq@WjF!5/&&2,*4/)-#!.;#2=!*Up*m3'n:.c90G:185 1 W T +/(! 1 +, '& -"1&-.)%&'/*,+&, +*/)($**,-/.-,-,.3/6-0 /3'1#-.!7369 7<%7?*0C&2H%=E)4I+A?1HG16Q*8?.;@'?E!;E%4G%1<"=?#=D#?K(GQ,;A.@I5MK!C^=//' (  >4 1DB>C/>P7AM7AE5G=@#,/F$!'>+>?H@,I .844;=$:BEK/#J96*&EG?0@/%06. ($9'/.G>;g,8">AAG6M,efW`L=GeO9CxZglR>f!. !+)"3&  +G+(0*#"$$*+9:=H9-3E*2B %!"P"Xrb~vxmddji:LimgiPo5l=R>J^YprAZx@hoLUdotV~usnwkXhXTynM:_CIRF_u=kyAW`L]EXcH|?1;#$"*F83KgT}nā2MὐwߔmgLm6yK"ȵ 1D,28mcw\ZSm;T04B&<82:/5 &)6$3584"PH%CS&'8:@QPS{AwIhNZ|^N`sGPVZewjBY@9H?5=,KS!ST6>&2>9(:,56+<8=H[57E*DS(V]$]W(y.u6RuAhAyRwSp{:GR).3%6=*GQ45DB?]H5G11=&(8,/44>>@Y5Wn$:G)[]6;.;!7A"'*1@/:%+2@"8K(Qa7ZwNVW[}GXe7L\#".%*=6JY:bl4Se*LH#BJ'HY*AK'('  ( &'/@>S|]w˯Wul:9C+C8BLDMM+@A-E<','$0&0743/8%@K)2;!#0+(;0!>2$53'*4'"&"*! -).--/+5)+(!-)$$,") &##(#/'0.10/.27+6#&0-(.-8*!89&N96r:/DK.N25>=#97%B9".5!+/"08!,9)24%45"-3!+.2+4./01215$22722/.,/213 0/0127!181472!/113151//1+4.1!/0(,-)*$@.1 %$&!)"0'1*1(/)0)!1%.$ ($ 0&/&!5*!0,#*$,%%$#(#!+'#,()%+*.')()%'&!+.#1)2,#:-%7/!6/7/!D,#=4!9- :-%4*B*>9"91'4351541.$;1"57)62(94'67:K+AI<6C)*>#57(1@.8@$:G#@G#@O16M24C)?F.BQ-?Q79Q4EM-JP,EP)GQ+:N+@9+;1>&=<'=9*[T-Ih;?L<5U1,:>*:,B+2I9,073"0B%2+$@0&T^%j"*Hv7RVbCSj(9KFL!1G.%4+dX2fd@J\17TP6EC50"+K"+%$ #)(',66"<905 13'yT%>k@=nzoUc]]]_peK_yPP=-;HY`7@TA7BIabLdqd}Ge4Ks~n>tgTjQZt||viL(78E5Y[&'8m0DYXjgQ߃ck?jy䚟Ӟwfhm_M?\$1~:>VeVVw2[p)>R56D3-8Z]|NJV*I\%0> )<23:=*KKbZ{@EW9[nQ`\Xd~=Pf-;J!4?1979'FD/9H1;@4CIDVgt[t>]14@!/=,@0A+9:M0=Q(6B2F!IX8@X9FW7J`>_q$Sb(DO"GM7>";F8@OZ1Zt/Ka48N;,$28!33,=!)4%D,'O8)N72h1.aI-k@2`N,?G4:5(5:"7<%:72< /7'40!)65632!7327$01!3/"62#03$99$4;"65%4: 8;#9>#9>)6B(8;#/=#7:%58"6?&4<"1;'4<$4A'6>(5;'<9/5&;5$9/(;0!<4%:1$63 --1'2/26 >:)=/$B7!95!60$92'+3+2+%[0<:"19#E<)9;+7=/6<'E?'DL+MM4=Q)A@)JK&BO21F09:+7G0DA2RG+E\+>W7HF(=F'I>?E5F'3)&.!1046(0*($/"'!# +!  +    G9@I$(S7$C   (0) 88<9%33'2?(,%+,+`Is{-uKSHTqhg~namiRP}=0J8C@ER"?U GYC\1WncHaI)=#%7%- %"   &%)$# +-1&4>'/< ,0ESJHJ~>XTx\5OMB=Bry@>n+rw5qLSk;DV#D\Fdk[sm]_q|lyelwpjR^snXY\s>rntыƇ̈0dhZ$I/!3+>A.vS{̻w]FfQQqLŝlСy``hI޻rtn!(3-.&)++C=L&#.,2=!7J"7M!;C!76<&:D89I5:H)9B!,-.84A5A'BS41E#@O9L,Wf>Sc7Yh8VfAfyDjz8Ua.Q^.Vd)K\.Wb2c}B`yASk)?O"8F&>T<&&,*%4/,8*AA    $>6FDGW6EO*:@6GTPK}5QN0859R7HV1T`EUd=SZ):3!(4)8H5GS3BK+9B'1;8>N9 +    + + + ' .0 'D Q ;!1%!41!.4$--!518377%7316!14"5,88#29&:1(660:'-1(40!77":7(59"-3'.-3127 .2!/5!4/%3*-1*2!4.24!(@"$6#$,3,.7/:#36'35 3-!36/;"0:")8&,. 341<-=">2!B?!8:'<=%4A,0B(5>'7E+9;':7#=<$X4"H7'J83R=6NB=JF?L<7\>/ID(B?/8A+32%95%6>%?=&::$=;%D:';@)>2&:7$;7);>&;;%<;)8:%E='GF)?D+6B,7='><&@H*>H%=F(4C*69#<:'98&9=,24*7:$.<)68)7>!9@$19 7=5B#9?#4I%:=#6;%6;!/=$66"-8 5:"4?'1?,.5 .4 .806"/9&36 46'2:!75$31&72 63"/)!6(6- 2*-(%1(6'';'"=3&=0(?3%<2':2$60$=,&<3>6 ?2H0!50+)!:+3?,?2#AE=; 8;B<H=*:B"?5J@#DJ'4A(36';7(9?%CA4>H)3M1=8+EB'AD):M,EH'>R+GJ4NL1AX*UJ0L[+HY7EX)GP$KH#R?'ER/GB-;C%17.<4)=AEC+>A%>J)7L.=?8=K40<+%5 $%$''"%#"    " ' *2       &52;>->;+%L("5/-#'0&)-=~\Pc[PmFF`3sW4Bv7 .;:4+MP]i$Tv(;X'5G%;I#Na/Le85O$4/'.!2*  &! #78)06.,7("+;8'cz@hqwG]zvqKad+ed=X|scx}dipFT|;W}RmnkpjKjb6Vx7EAKPD͋sg֒ΰV;S #+65411*.YVIe&;I'>L!?AHM$bX49N&-+%&*.$%>:SmAN]fGzj@vuGvruGuGkt=Nj3_j^p|IQlXK{[Y2Q]5TL.>S+-1%?920DC2HBWymoH\p4x>Wl:\pIiOtHI?>6cq+V[*ON IL%9B?DO1DC$FK"DJ,`^.?K&443;H;7G:0=*0(-#(+13:$>C NKC>55$IB<^S6ZU-MH8]RZVlp[n@CP'56(8K3XYNx|h=c`3CL-3.=LDIY=;H.2:,,40/@7,/$'  +  +  # 0;*'.M d L*D*$G0#@1#=/!@3<9 B5%I7#:D#==(A0$?5&C7'H9($.<(71#55#19%3;"6B&4C(H; U<)I@3DC0>'<=)?>)><'G5#R@ XE,P>8L9*L:6G>5]<3NN.SN>HB06=3;<(C>#PB+==&?@(M7)D;#D7,;:(C;/?;(==,GE+N=-K=+>F+A=)D<(>=07?(:<);F+;A.??+>B,;3-?<%@?*>J)A?()93'B:"@A&99(81#:3$0>)/<(05$34#-9"<1#;7 :8(@>%4F%8>$7>&2(4>$0B"38%3>$6C'4@$-6)87 6>#<=&8<%79#44&.+%80*=/)87'76(E- CC$D:#73'80(:/%9;+81!<,45$;5(>:":@%=8%>5%49$<8#FBU"=>gIDc|dwckaalkjklbWHaA8bWMhveMkUzg_`nE9hRSh:R\;LL4NB6P.;LH(9ZUo Wq"o&ED"=Ru/8I+8G1o~?G7|#\e#J`C88B!5EswM_9[TKV7Gp;Kdi߫ߋUlL:E"C><2"4/CF&9>=bpOPUk18;0TdRs[}Ya~mMdt08C@=1M1CR5Yq8Q^2Pd-JV0DN$GX,Qg/ht2Yp8`t2hv2mm,Se;Hb9JV*GP3O\:[hGZhJ_yOcTiJYsLcvQv]xY\m:NeE(:G4:?3?N.BT4QNolUEQdZVi;9H,4H2=L?5G67G+-4!-0$"3.r=,o?&d9'l)q)s.w,r.u.v.t0y/: ~<%|7'25?&>2F3D*C2I,N5G4H3V8I@D/D*I.K5xQ3TI8NB-BE&:B.9?':@%&8>'6<'9< ==&>3'N:FD"?D'D%GI)CK,D9*H7,GD3'@B(>D-A=2:8(::,?:)A:(P5$@E'9B%;?&>E)BC,I:/?A/F9)A<"BK,E?,FE+;>-@5,?B'@L)AC2DD,BM)CC/=G*DE3<6*>@&BC,CN,GC+>O'H=*>C%6D)7A 2:#<=#7:!:;%:A)4;.3B*47,9A'"CH$AF$H(G/EK(=O.CL'DQ1>K/;A)7:+A@/K>.FG*V9)cP*LT2CJ%>A%<>%:<$EC-AB'MK&LX&J]0`J>Oc9=I2QR4Tf-D]7FR7EX7JU:RQ;ZU(?T&88F/JWNIGT8P3&B"*,$#/!!+#' '),)1#(##(!1(" +   +  0-67"JD*??+(B" ""W1b^NY+&D$#8(45.hfY_ACX0]\:Wy55vTm4N+Yb(D]KRZhCsevg}:.;J9R~ao7db4lo7VYIiiJ_kGew\NUCjqJlrRgtOgt1Vf5Qg=M]?TlR`zTbzVPeapu]]pZKsLnuEnk0LQ>ET5CE05C3XW);H9Sm:^j2MY9GJ5bX.NS-TV>GO9<UAX@\D`D^E\IXJXEYGNAR@UHSLPHWJTJ[N^OZMRNMQKQKFJDQDMJWNRSMImH?RJ>:E7>H0@D*N)DM'H42H-9?'E=+JK2NG0HN4CJ8CG3?L3AK/@F.AE-BC.@3@G*=G-:@'8A*H?%BM3;P2=B*=G(>I'I=*@Q,:81;H&BD.?J&EG2BJ0=I-GI,@Z/H+@G*HD6?K4HG,?U+F+4B.>>'D21=-'0+@5'>=#@:&FD);N*>A39B!BG"BG)=O$7@+:@+1B#8;$-=#5;#6?%;B&=M'>J#R%=L.FP7DN+=N0BH6GT/GT.KX)_S,Tc1Ra)YZ0ZW3UN/ZA(]H$IT)DP0DF&LP)=N1PT3Oa8\UAZ`9Pc4fc>N_:U_:TS1Da.b[>]mH]uCip2bu4Rp5F]:;ND:M7EH'Dj9>A9=5A?2GCA" =(- '$ "4$61/2 '''.+$,3-018EO:4".3-KE&8C+(;'B9,t_}uANzMN(9QENj3l#IpE=V@R]F?_4Pn3AO$=RSc(Vg8i~=Tq-TV+Q`&ED<="+0($7 "3OFSTP_sb)Iy`W6it;mJ06`kp^}jPO8m_S_nEgjJLgqs}hSv[Qoa@SCHI7RqE/E.;FXKhCOoKEuQFW[CX@9Q/s|/by[*Nnzu}gt6j[tDwHJj<\_,|=)~7Mg,CkKAF:T[cD]SOC3XC-.:3>GRw`wgLXQ(qQ 9QOdnQ`{DpYVWע\hxt^Ɩs~gik\|Aêqa3A}BVc7LU;f[Ӌʜƺ[USI@o|?\ue]]MODipPoDS^8NZ>FSVdIGSL[qYznO;hxCjwAuICAnyOzGuWuadZSJSDsx;Zn4N\6K`HTnCZtG_qK^kENaUnhWRxO~yUo|SmxJspIxyQ|IuAls8aifa=V\8QY+UN#BH0JU5JR4H@(WK2dj2DR9C_LTl@GZ:ER3:I/5I?Rb/DA,ML;}w:as/DT'@C#24(8<,=C,>D$/1#/6']:M]=I]9K`6Kg@CkANa?Vk@RcARfCWjD^e?[j:[d>K_;Wm9Jb=Kb>Kb;W]4Bb:Lb9Nc>Pf>G2:E/8H,9I2AF*@M1>F(:@,=G(CB)AD->L/8F0/E.;<*5D!5B+M.?G+BG0BK/BO2GG0RN+BP.N/CJ1GM.?J7?K0?J'BG&MN2MS1FU8AQ6AJ/DM)FM.=O7AJ.AN.CL/>Q)8N,EI)>O.CK4IA9BP.IM3BT*EK,?M+6K-:*DH)?T1FF1DS*>X/LL-IT,?T1BJ.>F)B>$EF(EQ+@J,EX+>R(>A*DQ,OuCccKuvE\vILb*Wf&\/]l@gg&qp?T}=`^,7_5&7&)**",-&@29J79"9>)0C$-=+7 017M+;<"%@1-6F387?F/FR*92+$.)(IQ"ae={`\`Fx-bFHk?GX4JX%O\"qo+br8\{.Jt1*T3IF0^c!X`-bb&Wx2:T1hY8Wf(t_(sq%Wj2~o(WfOBW\Nd%G`B7P@7A.>M8Z]cj\mZv`jq{J|YQVtYOMFdHWddJqr4OTPXVVwBLg@HXP-<:@-#6%JC&g4X=DH8=R./F5+Ea'8J%3/(:(K;F@M]3?J-3/7IZ!MR%uwU\wzKaISU4ueNuCv4=I*D>,L!8@[kGpNJފc|ER_fmtWgDb}Ee~F[nD^{W`oyomZ|}TrWqvSY]Vs~SPr|Kw}KuvI`e=^bAig6e]9EJ1@S/STBXY7RT5CPSk}T`p9Xc=Xe$68&)0(/9->F" ' %3%>"5@!/< 2@#4=&0:"6<;C6= 9>!>B$:=!I09O1@F+=Q*7I09C1M+U7@J1?K2BI1?O.GM6FK3CN0KJ/F\.FK:AK7AU2>M0?R1FP4;M1=G0?R/GK+NR2JV1OZU,3L35E2=@(J*=I+=I,KL%FG.DN-G?1JE4;J2:D2=S,8H/:K,7I,>J+BL-?V,;T0?N-U4GS7DW8CZ1LT3A^4ES.NS-GK)AL'=K#DD$1H%9=$?G'=I(M)1N/8D"3A($>'%2*%1!.1O? CT)7G&)*(/E9=+9@<>K+=C MO!DT2QC?qQYo3FVJdKThNeu8kk+Y/w3]BhiW(XI|/OOX{j}8{4HzSYu\vr,F@,^7hFE^CCO-Fc2G^"_yE?T>IN7hq4=`;#B"DF"'5M?%as:Cd8JM9WZ9dj5Tmgb5oDY\hKto&^m=KbXQ_e:W5+3"02&E,.`UErQvœf_EOxyP\nZF]_G^g1MdCR`RYIj^R5d]?j^gz|V`RU.pr:vn[]nyW`~4vLP`F]lezSi="7=$59&A6";=#C8)A>%?G(CR/GQ0DK1EM3LK2EV.;I4EI-AM,BN+=P3=N46H/BJ*HK&JR+CW5HM5DP0=R1CW9GW:GN3EU4@Q0AG0=Q2AK2FR-HU6OS1FQ8GH7>P9@P:EM/IV1A]1?Q:9I09G-9D+>O2CV0VT4Hj;9R7H@+BS'AP.5J.AF-K//M->D4BL+DN:FX1BS0DP99P0P)7I,9A)D=&.I*5=1>J-7F3:K,G8FJ5JX1DV6AU-FT.CW1CY#DN(DN&DV+PR'LY7BU/A_.LP-O\8JV-Ea2OO4L`BSb;He;NN1YV;Yo*ak5TW8[Z0QT3GN1PY9NO6PTVjQQ]>hcJd{TqE_vL\oUZ`NeuX`rU``VXp8LV<7X&6: ASC:>$JG-NL.F^#8?*DqUDK-HC(=;-6[59646B3IN<]c257NEJZRL4V)8hG,@T61Jjn&Q=(8&#."*@27QjH^vLgYJse(GM2LX-PM;G^8@J34H36H76C79D+1/9 16!#/. F9'9<%A3(8=%(5-# 1 ? +4?46&+,+ 3Gw"/Z>0E3:###  +  + ' E IAEM$>M*?H3BI*?G,E?)EK)GI1AS0JP5PP-HO5IS/L^1OR8HP4FP0?V2?Q8DI5CM,OQ-NV,SX7;^8DL=;N+?F.EO4GT4KQ4EX7IP3DR1AT1EJ/ET-DT7NO7UO4PY6FV7EM6IR,CU4:P/:@55A&9<+@A(@H&LS/ST->^6CO<;J'9G,@O-7K,A@)FL-BR4@Q0;O0DQ7>W48X8AJ4KO7PV0Ha4B]5=U8MQ3I^2HW5DU/ES-F[6=X4AN0?Q.>I)=S77P*8F-=K-GQ.I/?D'Ld6Sh8EP/J^3.T,-5_<$a{ORmMbxM\vFss>sJjy7KYJ\cQmRvsNg;6OEhXb|YkuBhh1Kc_keOeLo[[v|x{~Toc?n@^}[Db4TL-]];;d-@N/.G):A(9E6SH`EV%?6)^2DM'! !,(9R,').;( &+#5'!.""*@_~}a{~:PS_m4`{SpJjyz\=I&@R6Vf5AQE7;BikC{q:Jlʉ救wy?Z~~U{Q=bW63!VS%wu/mִ:x9h"Bj*:vG\|Sgu_p|n|rC3G_MNzKAJWctaJhv^vgmX:_3Zg>;qR|͋yacWzJOs^qa`K\|7EL7GK_cc]_YZansj[YRUOglVn~T|YZfkdccLrMwM}N}~;fxGkxXRWci^tSpJkhp{qrxqvd]NuyRk}Rq~Qiv`golley[vTvySjsS[q^ipn^o{Mkv<\gKbjV\st{g]pD|{>]jAT]@&~T,bJ 25 ).60+99*89+0C,%7-$  3 +7"94!>:2C4.80*9/ C o })"W@5O1EE7MK+SY.S`/J[4M_I#NL%U\&Xc.Pd8Qb8Lc7JR6IU*@O1FO)/W,E>+>V*GK3MW3TV/F]6HQ1AQ7DK(IN0MN2=a9MQ4BS/@P-QT+Gd9MP0F\.O]1Pc5Bb8AO5BH(ER'FM,GR#@Y%=S+AP+=O+6L(K?*F['EV0LU3T`5B[4IW0O`2Cc5HZ:N^1B`9EV,KZ,JS4MP)CZ*FX.JN.FX1EZ3OV7I`6HY0CX/?F(GM/GP-AR.AL28L'8>$>M)BK/:U/=O17G+AB*7F%DF)CX.9O1CL-IO18W4=K.@L&7N46D/=G+;H+/D!>A+6<-7I-S/HD/AM,0A.5C.DK2E\==Q5AO1@U8>P;A[??JMS.OY;Od9Jh7T\CIcBQN4SeLCP/LX?W]3Th?al^^DloCZmHRW=\oQu~IoSn>`q*Na>cgRmyD{Pip\xBgy2s:O?GKL[bJiO^h:{r6KeQoP{^vn]FkqYlItrbj}^|RgbhnSUhS[OOORi+NgJp{:}K(1%2JI&+$-4325K=CF&:&6<* /8I1I.1SPJ3qID-@_T?T=ViNkj;GQ(AC5SpaXkwBpm=We-`_?sȆu=Tg3XpO[rXpuezw]~V\c{PPݓw{YX|txFBw|l~~pmwqeb^W_]YXks{`V\dutopq{`zo]V`XSRelnj|em}]wYtZqTnlsnv{`syo{fTxNyRvEu\fa{ojgzZV}Ix{Ej{Usmvm~d}Xf|RjoK`lDk}]RgS6KWn|ZfoPoy:U`=L[>O\0F=!,2 +2'>1/?6'I1;H1I@/IN.IP5PX7GW;PT8NJ7GG8KG3OO1yT.]2pw>HlAES@LMC\>QQ9UZ4UV5LW7PK4MP-MH3PU=FP0E@0`E,R-]f=I^DAU3WH-ab0Xb:MbHKZ@GX8HX6TM2JP1IN.BR4PK(>I1HL2MS4VX8NlV1YO/OuBF_5TX0Sl;Ob4R_2V]:Wl0T^3N`3R]2T]?We7KbPc-Pc3Qb9Xh6Ff4Q\5_c*Rk'Ja4P_1Md+QY5H[(>O.DN(QU4Gi0S^/Qd-Uf5Wg0Rc,Kb/Hb.:G'??&DJ)AL"BR*>R'BS)GV(BU18L.:G+AI':S01N2DC/;[3?P+?N&AQ#MR2PV/D_3@N/FO(BX-LW,MZ.KY6HT6?M9HW5CYDPW8K^9O\3Kb4WgOZ=UU:e]7nkTjmMtSuj=qAtI~}iySjHqNlRg(*-&( #&[J/4<)gbA^cXw\BT)hrV?> PU4zoQgAVb3K[GtvbnqmTN66<%(A5,jLj+6E/&@A:7C= [d5haZOZ,89#cgUmI{rA`~TnThQe1Rs(=Od=YgclWMl]?>M,V_>o]DPf~Xv>vwQgOWl^~Np]zbIw9\d?YrCbmNd{[Wh~K_KXhHTl~˒ghnwzxnunfbigeMsMr֕ӒÉǮmhlvltlb[Ugofd`ccpxpv\xV|JgmJd~UqSncew[_yfgeR\bl]ecmdhaWGtXz]k_\yd{WsvYRhN[xHmwKVi8NYQyh\t~Kmi;W_(5?*.?2 5,  +< @+ F<&@C3EA2A>3=62A-/V!"o' Q5/7.@0'.*/'(   6 <0<>EA/AG/J<4=M4@C;EI.=I3;?,>C0E>/DN/JS5YQ6OX:TXEMS6MT9ZR:NU1yS=ra8UmFR?QT1Na:G^;RS;JP9MQ:NX5I^?VS;aS3\m5Ud=R[8PQ9ZM2^d3Ya@N^ELQ5PK-LQ.NLFIQKFM6HL1XW1WS=N[DE^:5R7BD/PM(CX1TO1W_2TX2NM1NP,WW0TW.G^:GT3=W1Mi@L`&Ca<^S;Uv8VnAF`=OZ2cm7SrBMc9Ma<;U7HN5K_GLYJi;bX5\x4SpDTgCUw4Xg8Og8OZ1Oe4dhDi:T}<]dDRy:Fj@XZ6If.B^,TL-H],JO*HV-SW2Pc7J]-CZ'SW-Ob(HT.GX$?W*=S.=P&FM&DZ&DG7HY9NV.GY-GU,FZ/J_D_q;Li:FT5XZ4fk=Gw;[\;Ek1MX:O_>Zi>UlE_gEKnAU]^C~y=Nv=] 5|   /)'\`N$SMJ2B;NQLpFDp!':", *" / dV=R!6@ GBEU9FUOq~rh=eiooft@{9dh4iy)ys=윎ΓyjQ~,s9FAHEm|8Nb4Qd;YmM}KS:WS6PcAR[DNY;QT6Q\5V_<[cEObDNYAUS6a_8UZ8ZW9Ne2FX=MT2Q^/MaAEZ=?X8IM*OX&Ja6PZ2Di4X]9^\8Ng4F\9OU3Ob6NW6MZ,DY,AS#JO&K\(KZ0La9Ud9Fk7GW:L^5^d9Ru.Kb?Q\*Re2Od;Jd5TY:V]5Mm7Yc=XgCWg8Pc3Nc;X^2Yk=cqFWq7^i;Zn9MmAQiGThFHn6emBQyEShFRj?Wl6jh/kzNyD[V@Y_>QHIi>OcBVb;Wo6QkB\m>Wf=`o5[v:Qj>hp7[ueTER1BV+CM/DR;XvWg@lebgWn\ysxqkb~IWkN:XpXzxܱγʫvkuqlysi`j|h|}_}tuj}`|mяwyyssayRjOZpKlvKRX_UfukxeknuUm]vwlzdffotxi}[U>ht5_lRui_z\KQ^@RWVf{]djY`gCmh2ENIiO^sKsWyh^PY{SmQ9C.:F+2E'E*"+ " <BC>J.N8>G68J%>@$KA BQ;K)?L+>G,?H/EJ1a>DY2GY-EZ;GSQ_<]h=[v7Xr@Vq=Wo@eeJ`@Xg7Gu>gZ=`z4FfFKQ0PU0Lf2G^;UbA^s?vj=[y>Wj@]oGZoD^g?dvKZ{AcmOW|BY^>|f2N?TTc;iFbzFYkJan2Zs7[p?Ui7_q8.XdM;ZN>3zR2Z&DB&Pg"Tm3\uIq/nz*w5sb}y|dC_rSCOVpfK\}NfxenMXm,alCqFH8amLXhijXkIsoHy؅c傪ቀNQai>afDbwNnz>pAOh<+M\:Lh=KU5DQ5UjRxaJ|8{6T7^S}WcQEm0IU,gqPlcRفϓƒƑ斸Нx䍕jhYj|\{j`j`oiazauit~Sdpy{}}t]~VwEsCvRYY`[XMVsdqwteSwOll{Rx\loe_takUf{bi\S\}w}rjUYtLFR9QSJ^nL^`JhyLHh|\Uj{`fovyZw\tUj~P;C/=H07E-=&!!. B<@GC,DN,BM/AH)>H'E=(D) B""; 6)B)#LG%DL2EM2EJ*LI+QJ%EP,MM-RW,VU,IO/HK2KJ-QR/LR+EW6MN1IQ.XU3N`4dX5nZ4Y\>SZCSV:JX9UOAXW6`a0X`AR`>RaCLf?QUAHd@LYF\S3K_9UV=Ha2HU?DU8TS*Nc+NY5U[2H`.G\9AW2QT+I]3WY/\_3Ne7S^9KfL\8Ka2C`9X\*Rj6GcA\^:Ey4Nb7Sd4JcBJe4Le9DgKI]`m7[uCi{>Vs5Qv7YkCVrORpG\j4l{4T~Kbk:ZqBqr9v1NzJ[Y"f{A^LWvBan2gq7lu7T|5^p>or>fCdyDWq?am:Sq@Xt=aQ]uAnmH`DTx?esYQxBa[?eiouA_7bgBd~ERk?Wi?SmFZs9Vg=Nn9\k9T:nl>e2he9xEgIv}Ea?mW\~7bsCpIVr?a{JYyRqO|P^w]Zlw]s^wVqjtcgzllwdywzorwidqwӞp{Ǘt`~h`coe[Fy0[p$3u#EJu}/r/Km*R|T1S-/;/8L 7D&EJ0yN][Pj}}\zv_v=o\m~cwdYzБ͋zlzrxࣗʀuЇcawSiscxZmScWh[pa_|nzw\mp}jvkyllzak_qZW{WcuPg[lWTw\kogb{mKmQvaryexHczcNeWplxteikRnPhn~ocp{euFF_9AV9HJE`o>O]L^~ayT]wt~[mY\ckn}toZgXp]qL9?.@A.4C%>+$8 D*9C=?,DK,DO,BI/DO.CE-?)#6 0 . J GMEO*JM2KK/RR*VM.US2WX8`S;_U;OU8RN3US3\^=T`=Mc@YT>McV^;R_;RXBV]<\]D]U=T];ZMAP`4^Y2ZfV^CHZ:OR3NZ;R^R[5P`9Ka/NX.I`,IR3Oa1Ib5J^5Kc1VY0^d.Qi6Vb6Mf=J_?Ia6Cc;DT4IS+P_/J]4A[.IS.QU.A]0>U+LR*E].I_6Ye:WlBKj@G`=L\1Oe7F_8Ld0LW5Kb'fe4F7Ac9H\5M^1Ee8I[6Y`1@aN{FVx?^xLYz=zn=a?Vo>d/e=QO`p2Vv?roARLYqNVHWlBvKuMv:cRQwQcnj|9fGXgIb{MUsNLc:Yj1]uw=iEyLjSpsHXKcvQ\}KOvFpTtS|XJrLtCqM^DXg>ay@c6cr9k4s5\:fy=m7Y|:htLuZ`eXyRbsISuP_k8yS_DcsXyPsBn?aDAhcnZrR^ok{TlBwMlYlL_zGuSigxtf_}p}o~|issgt狌lyngtugSZlkscYvY|V^ŀmckMwM<*3:|/>(.OW-UC`w)$)kQh?qXT}^Q]L|P]O;ihD`UuaelHSmj=BK'l~Y;SUYMSa^sa_VleCҲfxmSkEUZ,S]%@LgMd]yCYI^7gu<],mq>YvOtR]^pWgjH_C^pteZOp&3: fu=lykRt=cTvrҋċҟwwNsHO\X|xᇾڈtw]w]]zQmÁoxfrKhHgrQpXYEBgPVzjZ]}lulzCYqTe~ckTkTzSdT_L\y[hc`Pax`n`tYreqdpRe}I_De{=cvFdzNz\b_trhiazXp[cnznkKgz^oReNqi`lnwklP^zRknvg^jUWp;T`;R_4KLGuHrEqW[|EIZhshB_wR^ucj[mUhhxmW:B)E>'.?()..$#)<@ELK)BI2LB.QH0IK0WP4TO7XJ@]M;TM=RM?LV7ISAITHaGWANT8]_=aZ?QWCLNMa7HZ4LU2I]1KV3H[/LX2R]+S]8Lb5^Y=Ym=OfWsGesA`TYJNrQ]v?vw3H^G^~9I|G_fF^D_}DTu?SsD_tB_BbZd{:rEg?h?j}HeQ^{AIA?]AUpEbn<_}:k?WDWtWltCwHjd6{[~`aosuQp_RrssBzMĕqvTTwB:L/BR=Le4T`{vUk^Tr|J:ZD\}O]^jǂvV~Nt;OcNavZYlNMiF]qwpIKjauAkOsOb~d;-5)-* - + ?% F7"*;.+3/8+$?5#2:+>4,<%#&#7H6O=$NH*KF1N9.LB4\@5pH._I;^LP0EL1FO/GP;>R7?L;:I2TI+rT-hW6YT?OY:LVNc?Tc1P_2Ohcz@UD\}\{Gbt?V?byR[}=[y=o|FkVv`|AbB\r=ax3tGmH_N_CUE)JB);O)@N0BT5HS.WV(R`5Ti6PgQi=^rAT{R]mHewU2JX0Sf3VlAlsM\P\wGc~GWK\{G\Pav>^t:S:cyAa8axCZvBdu@[qJtyGg;rRaGhAULwsHH}XqacM_}@XoDVu<]w?mzBPqH[p?VyMmpWmCcrKN?WiQNl6Im@RpGawRUNPsCaoGeqLe@[}<\w=fy>aL@zKgM\AQz@SrDpwBd>gKbFG]d>dUfRk|?hSeIbJZ;wBhBwHa2{}FH}PoIjKeNjvP^y;l}8zIeOtHeNn|CwMsTtV~\lRt]FXz]]]\wLf~sNSx@PyUpVYyP|YYZpOzZwSzGGm1v[v_{UjoTqLiGf~BkGrBn]rWosuyipmjmkTuzjrbsffe}\O@@Fl;\sQYmgSYVGVLuK\UnWoslhdw=LR3GS6HP;BI8?N-9B7ayE\q'JX7ZmSXO|Vs]wr{xethtXzYqtixQcY]}Rj~DfvHhJeS`tQSjCKk?Kh?_xClRywkvaima[jHQZ1FT6Tc8JT'5A:WkV\yfl[r~Tg}T^pHTmJiF]r@ZjGeMm}@`uKOpAFaI#&#) +   $",$2$%$)5.97$4;,0;+6:&;9'I:'_H*RG]p]l?Iu4Wa8Ae-FW0Na,Fb;_]8as.dtGTKJtQPiBRc3Li6Ijvz@]J^v9imtaxlVLZqBi4Ls6QqE\sBYFzJbpWSb};\OVA^~NcF[=nF_@v~SSGzN{Mf\bQzJc9cu=[zCRz^jAEtFhNbJjIcQ`]wEuovZoTcS}cnL{QW}Ybue~[O|VvM~VuxSrzMn|XYSoD{|]qMzFyT{aIqHz@bDL?pLIKDR^mac\PPPV[gZ~VlwrqUoFvf|]St[]f|jq~V{P~lwvne|h|aSu|zkŖ菱œvsrlQFeŊukaaKRt[|qnnvvʂnkmkdي⹺up|zvႰdnuRcmucԂVtr6NupWKE@}jlp\~}txCt{Ed}dYoaksrre}h^i`oVgj~eaUiOlGpq^yNU:\N`}a]gJVh8E\ICXG_o-Zx^|btsc`zYGbAfSvfYthVù^Vpcs܇JgQmzYun􍕧WZuV\jN]Tqmr]atV~PNnx5Zr4upEWo[XoH\kEG]@BO@Uf=P]5R^;|}3S^1Pi5hq/?YUZggɁ{bZ]l{c{khzyq~Xd{Qi|VkedgtkNlHKd@GcH\pDKcLdzDmt;Qh9_p9`l>UpLcwdxNfYtVoHm~Afu6U`5Ic2JX.CL<\qL`z_v^lwN_hJZi=TcMe}MhGk@dYnQd}CUn=Kb=     !+ 3#"# ",2(.401$.3&6/$0:=5!Z6#];*V<8DE1DA2E8#J?+R?'HY+BO9IF.HC%HI$NF1VK+GI,EE1GH)MM-@P(CM.QN-G]2LR8JX0MQ2L]3FS4@S*=R0EN(GT'MY1JZ.G[0\T1Le8JW:BQ2PO)Q[/Mb5J`*Hb/D]4FZ.W^0N^0Xe7Vh3Sd6YZ5Bp0K^=La2Pe1J`)Vc.gl:JNOnJPg:Sg9Up>Qt?\o@Yz8PqTtGXkFbpGq{;V<_rC_z:WyXm=]m;i|DSAYl>`8On=Zt>cvDawHmZ^QyOcLmQ~MfaY:_tuK|ejbg;xbm|mKmXXn:\NjLDhGH]@dFo5e/NgAHcE\sJlL^gjuKmRzkzfrvqrhkZjTwg^bSmd~r_ZvQkYw>dRwTb|:?TCbkF[dhqRV`=>TCJaA*BC&@C,EG,PH6@W*E@0=I)AD/BP,>E0H>.YI+QJ2HK3Um6Xj7Wv?mwC`ht@[CfwH\:L=Yl9]}?jMm`oXbId}Gm|J_DRtO(3G#7K,HYGNe@P`8UnH_zJ[{Nks[cVkSUnI_wEB^6@V3Rc=]j6S]0QW:Ti8F`NdnxIvKfvO_rC]ZIObO]uLg;Rm;8OYGvs~        #.!$& ""&&(&$'&(,,*./3:5 E>&2K>/?D*9G-@C*IB,=U0>=/=F)@J0AM/@G,LG-TK-ST6GU29N6GC0JS/JV:OO.LT1MW2MX3T[-TS3QX0Yc;Ke:E`;PY4RZ+Rb;Fb?S]2Od3Z\0Mn3[`8`r7Vrf?p}G@t@\VdvMg|EpGjD`Jm{FTPauUkuGfTLZubmFmGpRmGHiWaHn;cyOaH[DW6k{Kn;r`m@JJ}Gd=l@iCPhKcCd~GYDg{G]>eu=c;jA]{=~z2qle9vGdF[NsuAcEvCFlDnJy5Oe8gJR5PgJZvNkTTv=ci?V{BWpOvj?_YcMpzFuGdFT_rtJm;wX};~=nXnH\Y]ZWRf|RrFrU{S`gn~BhB^\oI?rMq\W{\u^uq\hj\{FGap[_IfzNi}B|TWOanFe@bzsBe@es~whN_}HfBtQjK|AiGh5dB^=FSL|tiViBsBLMAzVSbCSIFEuvKlW}s{fyťSeJtLm5x[V}TMkLzcMwBkuJQwVYawI}w|e}Y^[OjWXwVvOwb~\ztgbCnGeH|Cb{SjqWhk[v\zWsG{`yY}fU|Wz_gmqV|mgroti~eZqIWt>yBOLQʃÝc\fь؜ܫnZa[V^ڍchMp_[DDJhCq?c:b}@sU^dgPe]nfzIv[qm\gkOoDcJoRukiF%GD'9K#9G,HC)MG(ML/QR2UE4UT1TV;M[;IT9YY1d^:wlC_gAzf>Z>HiGO[Xs:[lAih6et>g?pNX|LW{\auPMy]WhCcpPX}Gct@TpB^i;\n?^{BT|A[o>TuCWqHU|P_tLMp@Vr>RpHMq2Xq@YzF]I@~HRh@ZsCgtC]B`UT;Ov@Bc<\d3i}9y>yBrJlP[CekHfDdSo\HUnec^[SiS]QY|JSz@hs>wL\CyPWSxWrFkMsH\HwMuW{RKap\VzSbGL|emb\~\JFEg_[|:fAnLyDpNtJ=p[|AbFkCe\Obu@`zBd|9z>C]FVzGe>@WfvJTO]ue~e_cnL<@brk[wgGwXjP}PM^~V\GE|dWSFZ~IptJGx_VW|L~OAVQfHwSQwNmIPdWv|FJvUjDeHnrOFmHGt`nSiLLkPiOtKt\qL|Rhu[vIX~Œmlukz}s}oR?^Ā܊ƧiyuboČし|ꘋχ~jnYrZjlv~˭랿Ƀ͆bTW^jx鏨ܟ\f}X}[ThQHZvPuQuR{`f^jZnR^Xpck8Qo3Jk6^{?[sO_hcg`hGrSjTX~Ila~mywVpQx\zPkEiGjPhDXzYlVl]wul_VyStK\KkTwNeT^DLr;nSeTgQbYiUhgc[wgjigumNj\n~TwXttXewVnYiUhA\sQv`EcuJSZ4NY0EYNRzॳencnilB{LNp\xPRgCNgEBW9KeKQtTkKYjRyMmvAPk\WzPYf0NfKTo % - %!"! "+ 6 3'4'0$*"% %*$%#$$'#)&$!'%(2-+8"%5"4+75#=9%8;,7@+>A%>?":B&>K-BF1E@)VK-OW/LT:QR2VS6US1O`:EX?RV0]X3i`;_mEhgHXzI]iPck8dl6cg;Rj9NgDKfAO\9]_\=vv@lFaQ]rA]rEUx?OpKei1Z~=[t6dy:XAQxIHnKTl7ak0bAPJ\sMZyAZzA\i|XvMhR_ayKlYt]mTcMfuEnA[[t^jsUI}gc[zi]vrr\cbw|al;uEjMxLzKbNeyMbuC`x@~:sYkRhTA[SI듅Uvaqy~\dYphwoSpuYvKnPnHrT{`seqbzkyedh~gUuV_Ydrwiޖ˂obm̀犠ҁ̈dwVaZyfjfp_ezcos]laehg`wg{SlqXjTnPQ~WPSO\iKiLqDm@iDZzHo<[1>e0Fe9VoBg^fJlMffSuTVQzb\v\nO`MiLp[CyMqCe@jDnQmQY~Umlb_KwOeEZCpGh}BOx9Go4?e/OuFOyFYbh=Zu]++,163!.%(&'#)#0$>,61 0-!(&&# %+(+ &,*&,,&,..#04*=2,C&07&<7691B%/9'7;)6?#4=&@B/@'WK-\V,QU:SR9RR6]W1]_2Rj]dE\f=li8y{6i|@evALy=S\CLc4D\3XT3_c,Ul;Tj9Th7Uk8Vb2Yj9_xBWyGelHhx?nD\IZ{J`z=l~=]KZv7[u.onDdEoJmJ`GqyA`KT[bnKsvE^@pvOi}3h{Fx|FaGXrZkvD^CquImEvPPZd{ZOtybbuBqWe]_GgNiznyChn{DtUS_zToP}@g\`nY{j{A]=hXrtetaoj}_hPYQ^OykeVw}EuI]9oETjc^oNL{`xOvpvxrMJQ^N^nBfQqGu9dmowYXz]vUivU|PfCxGZUdKFtOY=pKePCUyb[{\ixNfyvQAd\N_YrMR}V~ImbE[\uOL;uza^_|?PPJ{KHvK>QsVmIm?KJFRvRlTDfOuHxTsOyTqBl>lAhET|BLvUw\|RpSpJ_KlATeaw_cQZcdwV~i|J~P~P|^hcWngftfovru˃lmsPցqnrpiYd~qgeyfk\n_snPb~˃X_lMOUZ}T|PeKiQnNnVr]iU{SySp?pI}NhBniHq=b0a3n>\Cg>iRgVjZs\vNs`KpNxLuJoGwPmP|baReJgR|Z{LkKa?kRoZhIfG]M^9S~?Fh4Df1Lo,Lf*Il4\x9b:Wy:Hr8Qr;EgAEa3=S>\{XKtCIa@Ld_qJb{HcvE_xMlQ\rJa{GYuHMr9av9SrBeQn}`irtwχ~xbe‘jVq^hhp]nP^y?WeJgKf}BY~]{Ccv,D[6AaZ^|xo>]eRhwmVu\`t=OlG.2$3$('0"1'6&1& :(97:1!,4)-"%'$%"#&(*##*&&3%11'9#(,$.#:2A> C? 9F&L8(OQ>L5;H2A>,<>";C(9?*9<&:>#@C'BE'BD%FF*NJ*PY0UU;\[:jW6md6cm0SsFYa[r:duGevFeyHhn=O|KT`?Xe3jo:^|D[jy@OaVuYxKKJqTzTsLupYT|`RWvtmJtNpNm][T^zEh~:BSKdsV{rse~^uDTwOqFrYl^p[dHo?iQq\s]w>RO`O{EuG{Cs_hf~uJ[|HPdAnk^LSVzgzw|fpON`dbWRfaj^SgrqF`EjK}RlYkDtS}?qVpU`If\]eC^k`wOx^gczF~=yFY]~9Rv7U{;\v5Qe+DX+H[8OuXfeuY@bwSlN^}@ZOVsJiEiyH[}?^sBjtHwpˮ{Q~Tjszkbq[{ƚvzYjO]U__tV[{EarPjO^GR^[xGK[2B_;NiWh~wgzew}z~Ja\KS`J@S?-LELkJ1,3+..+1#5, ?-A7"G:&G=&AH)=9-79%<:"8@ 86 105+B2(E!87,J=IV9V3;<)C>QE H\,EB.WC0XN.NW0KO?(GD%TC#^T,K_3S[7^b2c^=tl?SC[fP]eAVsAYm?_f9ep=Ty?_h?`q9_s=ZrGRwLduIa|WluJ_{Cr{HtD`{Ijh<\z>ze=s/lC]JRG`lA]|EazNhtAyC~MlS\YZp>oUVSak_{M}@QcW~RyOqZ|XxUEEDzPqPlQhEiMrOX~iMtMb>mPeWuSvHlNKwgXkgk=mC\IjLiPgkDVHlRsRrDsStLYxJw^vemFTremR_zPLwSk˔w~\lr[w\kvz[^fis[vne[qfvhwUtXj^sdScIeMyQc\eOpSi^bMnNsL^}<`}BaIW~ScLuWdH^JeI`Ie=\}ClWgDfKhKcOyKٯIwIcJ^DaIgI\KeGbJtEiOmXfMoGuV_HhNkUgWaJb?^Ca|:\w=`|9]|AaC]|Am:^{3cFd>Xv>Ur8Puk~3Vf6Vk6SgAeQhW_PdSqLjBulo|MxKr{ByuCjrDtjِUwX]eSpcl_yfpɨ򙞵upgiWXGm]|dMZt@cFb6d;VUo<)O>X?$xS*hk5bt>\uTnuIpVO>aeZyR{|My?tHoMlHk~?CepoCKkRrhByOkOnMhZcHfhqmqU[GteuSo]iYyMj_}fo^vXD>aPX;=rSw\nPmXM|ppI@Z]YZpcwr\o>JUdRP~Z^`HMXxZn~}WFmoVA^^õ[yRWqVAwnM]gP]EyEtaSMtQhRbWdBl/l?KwUhMeFi|>sC{\BUonL}?ZNf>XM}AdKqIiG~iw[{NxLx>转iRyNRl@Re]nSkR^7wwFyGgs}nLrGpSauRVRé;>VnQgwYcyMI{EOjEAkiGC{EtL^x_q\k?wTcSEGzZ{KnFuUzErbm]qRmAf<=BgGh}=iPfHfCpDyHg>p=Q|VuPeOqavH?~C:vHyHnLnN_E_UtNoOUO}Pw^LRwOyJlZwZyc|guUQpbrMbwZvRUOp^wx]uZhD^8qGnGZtNhFjL|SpJ\LlEYArNuXa>eHlJc=]}?qCK[|1q@c~@LJjLs=]8aEmB@rDqFe=[~?c?a~Gh?gI_AX~:Pz9[7^:[?]@\FVJ_BfDbKpTaHf=Y{KcK[GrQnDiJ`~>c>^?\}>X:b~JpEc9c>Xr9Vt2Ai.Mr1Qt4Uo-Uq5Y~EnFwF{AyCrIpEtKhKgzG_xChti]USJP̬罻ܒKi8`XbEHvEL|NXl`z̚QcK[xOcWf}Vg@ay;\?YH\~VdIlR_fiwoqQ^nCEF6=C:HS7DWL`v^iu_bG%fN.jn6UsGT]C[`;W_TlH`T@lY,hc8_pDafDd_6ci3hsDeijf>`kGXZBNV3fO+mX-v]0}s;iJetT^rEjp@aOaqRPh<]dB[s=o[9lv>o{VuJTKTpKRlcq;rDsJjHcJUyHVaIld:Sw@hq[f}@S{BblBexFe}KXK]sGeqD\I^O`}MiB~@jUmxMquAyyHazAf}EqvMonHO]VZm<[l6U]7Ra.Qh+jl2W:YlBdp@fk5Vr3bn=_4J{OYl;_6q}AmDeWZP[s;h|3b4b@g=?iIpN~X[Kgg|>s8yMdM{\tGSVjsF\JwHhUo[AoSpLCQFpOykrgQ|iigmUEYrck_nKAfkWg_}`pGY5ovHZ~QrJfTzRbWf_o]zg}Zwr]WmDcZyMdna.nsowqm\_~t`b[T}In^rNtW_puhbWXĹU[|WvOQT}cKQGRzVKSYpqgVYI3b=Y|9Vj^oguKwPz6]KIJo~[u@Bw>i|6g<\Lq;aTgH|P_r[yHenuKd]DQzWqBzG^Zf\o;vUZkkYWt7ZHyj]VIP_rsgRjO_>tHpJo_KzfKYd|LL|a~LlM^oRiCzNxKpLwMxPuSiCcV`KiNl8iRd?g>fHhU}Lb@NzBl;mGfFiFo?knxVqWilhUTqcjQwX}LcLyWAKd>dBCi[_VyXz[yTmW]NeNnCdLuM~TXvT|FxU{SUlnxN[XpIzUPpFA>iI~Ke}ZI|JpVWxEPo:yL|SJErD;cu9{ILl?oLCʚDnRo?u[E[};a=dAz^\5f\9id;mc?e^@UeDXd;Me9S\4WY-W`?YY5]e9`qEaaHeg>ai:yfCozD]tEfb>`l:_kFn`@rmCmm:YrHdjCml7oh8oi8eg:auERLddRVsCbmDdtFVuRRj?[lBVt9lf@fv?l{Ox}L]P]uRee:Ym9\u>\q<]pBMm@Vl=Zj8it@fYbwUhp=hy4ZzB\uIVv@Zn>uplx@iJfXQhD_O`GjKkWsM~LzPxV`WjyLYFx_w}`fwedHdkzy_~@[~X[kQ[[zq}izt]FweNcvnup\XRIs^ZT~ZhZw9gIR9u>IGvb]uIqRilcTMwWbYo^{FkGFj6DmL;RHoFj0~MgRpGmF|Cy]qQqQoLpMx;tDhErK[tAlM~PnmWS||~KQt^oFbOgCmUoL}:KݭFIOsNxNzWepZw[cgJVC{EY]rS>oFlOw=i@o@gCe7jFq-\CzLi>Hr?hL|Mx?zKmExBtLsEqApHd4_AgPnElFp[jJR}6l@g?sOhBfG`]oRulcd@oFzHh>pHf9zU@vCrd|Q_UsQ}UkxTbyxpm~GcZJe]v^Sqom\tnW|nTZY^SR\H^hIwBzpGjpWQUJMROEq7`z>|=IHtJs:y=pAv=o?g?nA_NH`}ll<[|3^DdBo>a7V>gNmAjCnBaa?TAX7[8j3b6d~9_{3\~=_?lDm>|EoCdH{Fy@TcWPUqd`[\wm{ƃŋɇb>U{/Hg1M:PUq/Gj=`iDZoNYvOQsIoReJEdMFdHSn:E_DMgBiM|H}?j+Ok7FhJaOa}OapBOeETqAT\)>L:5>;8E9>M2;P8`?t?`fRc]VbHi\9uh0|}DsKksPljChqCXsB]a:g[:lk@^nE]hF[d?S_5`a6\a1Ra=Q_:b\4cp=feFtg<\r>`jMdrCWtI[gAab8he?_8|{BfqIYlK_b4[a;oe@pw=`DhqUXwK_iH\v>_sFPqG]bFXr=]q>hp@f|CdyPjNm}HZ|M\kFhi8jp5e{EfuAYyFDsAP^;Ve,as:huCctSrmFr@bU^yN^?ZnApx;[IivKOwCOnDLl8[`/_k-]{;bxIeq?hMYAbzd]~@_}OVrOSu@PpHqeCpIfzJmu:uhKR{^n[gOb?oM{>z`cXOnIC{dy~JX`j]~bmck̳KVRlȁ{sZQQrLHn8eyFYZ{m?nb_vQaoR\orupQWRZi[heZw~KId[VTGjK^SoNnUuezcWjk[JlHmPn|c<}~S~b}|vw\f\nԥWMAoLD[\H`}ViLOkibLlVtapeIR?nJ_Em?pakLOmvf`sQxYweM<`gE]W}U~TZfXRf_qQIyFx@vAmHvIoRvHeCrTZNh<|RlMjCyTsMqS|I[hW]sZSm]>GIvFq@{9j3oLNyrOItPBuqKCRgHSuPtR|O]KmO|Y^:rIv=n>]cFc?iB}QJqQg>mIq7s8y?jBVv^tGr_<`?fDiCiAfAfOhGvIx@uDo@f>ci@c?]YlF`hI^d?\g>be=Ti8Ql;H]8[V0_f2kbDcc9YnBRiMQh;T]:Ne=ad;ng8gg<]xG[aMUlB\[:Un:vc=eA_YqjR]G[rK_qJRyHKfJY_;Xn:X|F^|A[{JavVp~MmwN\~K^rLmy8xz5tv=cKn~RusF5NYAhreoYh_osJvhn{dMSy~j>MoS_Kt;@]yVOZcjLq4mu\bmE{BtjvEX_NNvPlTkUzGuHnCqChIsSpY]hapHsPsZ_Msm~XWssbwZuLpԅ{HMFXL~OoHs8{W=}Kg@g>iIg:o9sUkd`8gFq@|=LŠ{k?dH@~_|KxQV~[u@~=^AgFrpRsMr?lM|DkEsHuOxYZJk^AgqDeiIdnFowFiThpRcpDdpAouJdqJnhFfzQaJzJ:pghcdzJwMtIb\UT^rAZ|HeyEdr=s?bD[Nd~FbHW}QurDe__PTO[i7H6Oh6Uk4yx0|CpUwDr:qOpN[TdQCn[gPgBkZ\LYzIpDOqRtxPqScSg|SpEt@b]ZRM|KupBmCqAqDDi~seƼۓ^TVvdы]g_wGUqNcr|mjږlSMu`Hvсqe{Ts}uWdxotft[t@wpX[nixWn}myhJmvE@Jra{Y^Nr_Xi_IykslgLzQnflTTmxmZse^F|_\KlOtCpG@{9GK`bjOTgSmMrP\J~NmB|AmIhzEk>p@wShSgDYLjMgRVm[]kuXuGhMy=vG~zs{[S{^k;gHFyLZfE_;uyB^F]7gDW|EbCdE_BeUgEl^/tq7eNbl^`iLgp>bj:Ro<[e:Tr5QoE_l8l5TJdp]_Jv|[zGwSaW`}XUsH\kA\t>]{Ghx9v:pJjTWRnba^]rQso@jOyLVO`z]pKT[{^dk`|`Ggp@^=dzSazGyt=^S^vddwJdKcU|}DiLjX_Qs8mX\~Mme]Rw^_Mo}KuBmJhU{M|NxXXQ`wGKjbPoFdO_8mEc9bCn[o9l>kCeQjS{HqC^A[Fi=eFuEtcrPadsOtRc_FrRcLwSqMdLiI}]MlA\MW3WyC`:Y,Y@d9hInBiHPVb4gjvHi;h7uXrE|GxW~N]ZdztVhZ_A{V|`tn_v`_q[iRlMud[g=nMvTeyq{Za>^sBY[Tucd`p]rSozR|e]_OlD\EYA]EkAiHdGfGjPvZpTlZvblgkmhnw`R^PsMoPrOnLvVsQyT~UsQpJ}dǠTrBlXS~npbRySUZiZQWenn|p[lrke|²hViCYk=VoMsBD~7Mb'AQ0=T71D3'?@7OVAYE:ZAIeOg{LmN_uQhAq?jZ~`j{J`jITf9CV5C_@[pLb{?Yl6OhGLpMd?f{AYk<

Xu}ZrnseJaoqJkZ`Pyf4fJba>^r9s@zFgcgyfmw~SXklc^ntpbdBQR|hR[PQ|Slm;~8^SwEXeVj}WeWKtnd^thTWrF|v|fh_YzpcSx4֨:^ѧV垜˻XıghpjvF}QVireB9hE*җv吝rc\^ju7CnZפa쵕yYNy^Ioǧ;Z|__pCxHKAnXS#nv07_tSXGmoCaNBbcXg]Xgvh;QybWw]]}Qhj7>]u]eItZgtcu@ݩongP먍^}LoVMDcEϮ4ؐbpsjie˫t2a,r-eSJtHr^TkDpR|tU{d{ApӂfXTw>_y,B_jRf3wiQghyZ}RetgwC}MNjQt\ytMU`mx]ceI6Sa*bm.Wd:YDt=mRٲX~Dr*\x^hH|[~\t~?qu~Qn85zLݗX^ՆލullVted7pBBQnj⠀eqzfDXQDwօԒXg~?j8=dPrPdKvXX|oDReSMbĬooOfadZ={tg5`KrFTnxMRm{z[d^[q{qc@ocy^xBs^SYOK`pvmzmrr~tnBVZ`X|ZtR^vymYHsd}@wKcx\y=d{QihbcQ\JarNiIeMdEsk`|[sd\~RsQ|EOgLcGrYP_Ll~Xe[^cEd[{YcdMb3Ku1VtEd|>YsAnCw7]|YxaCZVlFwOk=eLawLfXgXV|FUKsGo`|eW_WlYi_Xxp_qNpHa\oRma\S`Ln0|b_qVqQN:mb7m4`o|Uz{;lSuw:q~FfLkuVMd`qzrNu7IfȚNݱหǽ[wyW٨弍vtjFkUyihGwSzYm`tSZhCejT=TxSxwqXlJYkx]}CtB}tw[kY`nu}kRxxbHgmknR]9b|Vccw9^tw]j:_HvOqQtDcl>jIN|D_@dJd~Me;kyS{jw]bQqMvUhKLr:X}9}OXr@j5^HWf|_[[b^tFe{;]r@]|=ZDfQcL`XztKSx:eues|Ilf?nCYz1=a:GkJX`h{~voHtVclyerW[k{DtGVx;axBZn:Um@]u@izBYy@Oo>UwWvbwn^uKQoUjXgI`La8WRx_^{B`}XXge~drPuR|PnPeWqGaQhKbSj]tF^@]MgM[hnKf_Qw[ye{waLji_pog`JeRk}Neje[gBOiAHh8MTBYX/Zp7OeHNiDVZ;Ph6Im=M_?P`<]c?Uo:qfAwMqdsawVo[jX}yEpT\WauOnl=mmLJjSddrOF^`KrpnYAme+`>nCy:{YotXXd[DKUaTloBnoS]J^t?kwLU^TpRup@~2FUj^mX|chqWVxRsQwnEnTrcl[do[nILvRxb>mSXqjkKu8~NÒGsrp`^UkyRpHWe]rWGlxlwmUaSx;Zl^ԅl׎Jn䘤sax^žMllRsX;7h_cc{qdsyw}Q4Rg=mG_sOh8YI|}<~AhnڋqUH|*ͬQ𿡿]|icA\rdsnn9IgutnUbKt9qCQDQs烔aKqWpm lAi7l>_7YtG[h$g{$MƄQǀitK{>YKy~[lu{YK[PEXy]i:Um2'vXֈUIyftztbTYd-|1QvP'D͚bv@^?]?SuVB`*@R)a|S[zFg-yj6M|k?OtPKmyVb@IUl|ept̓ÝhŽcci_4aSbAv7CmC{vmy9Ltyg׳RXe_ųDlKo]EYudVgzKPqdPypp>ccy?jc{~fO_\s>vZqx:zńxT{v\^tnZT`RXlc]ptse{F]zp_hac̆qpsSkpd^p~Dj{qۑ{oUh?wSlI`{vj˶{eey_:}k^TxF{T_x]~judk_ĦȆ|veqIbPeNe}c]>yJi|F_w=`yDhyNQcx\dS}JyeViGxAsTbKWi~GxEvgsyDhF\BlDiCSk8dWfPs^zRsVkVYL\yCeAvVUq4j{FqS{os|vZdyCcW]uDZm9hwFVp9`rFfyPcAjQeci^{2UzAW{bsDlsa[y9B_>Gb'E`+Jb1Gh7[{Zn]mrjKfCLbCi_eP`TzpXmR`vTh|J\uDa{Mfw7lFuGl{Py[|MezKd[dXeTiXkVp`{`Zx^VXOc~6[vKrglvp^yKg[mjLdGuRvT~XiH\{AV{L]vHoOvJgThtw[s{\vTm\o|gY{b`onNeMtKm_`[\qEPf@Qe>[]?Sm4IjCQR;E]5L_6[Z5Kx?ReERT8Rb7G[3LSCSd;oaMo]ifXU_z[neImq?O\Nv[J`4fZ3o;|K`kfwPbz?rTaUdkFd.b>qU\{=UmMWbH6iATI.Wg#JiHGYFN_KT^?\l?wo=nM[bfyJClTcyXPraSg:ciCbKSTbs\]hmTRxV:bG:[9M,0Qr~eSEgvzfiZWsKPt;YmAIqQk8{1}Y{EPqbcCNCq0B▫nƍ[drPcazGÁorP^Osk_>}1kmg֖jmhm^]CmsmiL{_oPwHr7^9{<9ͯ|docxP_{y۩d|֞SxdǜXVoo/eHbRn[0t@xIfF{SogMkrwnheo\hbC_cVoid1J~ÍPbv`yM4^GhlAQqxӠQvjd3s}4oOm}rLPc?YQcg8_.9Ӏr]:YȨĝpbZLzSgW[ricVu7XkVl=`vKP}?sz-\opGrG]cx=X\=!V{>HT^n3nrz{^UivcMlVRbc2a{J?qlIocsM@CzpKg}2{tz7ԧb}:yFJ|T@o㧤ǝ͚ubmw~-fpqcI_vWOv=|gyxFwDfetb\?P`WJkzVryG|IPʑ¦Mn?~\le[T}cc_dMcXT6O'\jAMddZd31c:Ra?g|Ur]ÙarD|IzLwROXu~sMmkoxCvocVqyUpWMl4gOefyCufyTOխs`\_c^~JU^off[̭[]VnAd{OzErMmzYl8k;[eV`yLSn6]{ImU{ShTgNt_\Bn>WoUCct@VrX:X|?af7gm(SBXg;\u;g~9V@HoFLdNs_SS|[b]n]uFZyVz[Th3Mi:gR2w8K~yba\EpNoKBnsTjR}:i4kGjzhai@n)udeɍڣoX}IBv׼dZgݥt{Y\~~\KpXmɯf[ßZv݉@tRbInٚ:_e{2dHU|/k`7oGxY}ZpPkm^;c8~1Ŵhw^tFy̬MrclfXWfXEI\oZ}1jo*vx|wriz]Kzr/nTwEVmF]Qy$s[h|8t6,sŋ}sahfprjquk[mNWwIiX~~FW_1wf:lnEiw;RAH\Da\4|dXrVOTu6Nu,^D_EsAc}YgVfQcC`tTbMmKqRkFgDpS~d~W]c>aEdDUt6g|AaT{LfGlUsKsPfMwQTmDwSRr|HkISi1]nGmAT|9wRsp|cGZl6U\,?P4`iLdvbpoMmEMUSxRa{tİ{ww^yV|mLnlYfz]ji|latVv\~k{vvq@lTwHgZrKsQx[~NuS]_AWwPt}}ryOmW~N{PuX~kr`\|Il\qf}TsLjMkPoUef6_n0_g;]|DUnKRcKJY>Xd2Ru2Uj>Oc9PcH]e>af5Lt?BdCKV6A[/NQ8[]/>xHKTIW[+_u-[wDO{INj;Wn4Yf6q_1v;Sxdwk}XikeXYPVpEof?w5kLhn~qC|CnVcwV[w>wx?fP]W\rAc=Do|pnUzDZ_]q]d,h?ws;vEEuuaymUgqj@}1fHvMDot`rW|}YbOYqEpaVan[t][qN>iLdv>RMh9OgRvF]]FvFRuCYv-P|+zuI^Dc=xEObWTp+`d$w@v8[r^/]~'(VgZ\SfwlbWp3xTlPpX|@u,A=jLm=T'a7yL}BP9`w6U~4xd(iKuuYgEkĉm|jiN6|ZkqkdpgGoxXk0]HҶ<헎ɺ0~FsH_o6x/[IQX9`i[bwVH{=^o>yTVQqWg:QnJyPȐoa7Vd?^5H3u5tv4DwPOJ_LLE`f9d]kcbOe8iwXvgqXcCzGqOjLne;dsBmTliL=lMߧftBx5gUfLTyxLvGsudR{Lbchhd|{P\́aufqfw`n]xTf|GhNY:eLxRyNbz?ZsApcu[~QmEm@qD_URwNzbudwYiYuLaAh?XZbOyRxEtEYy<_{BrIgp=^vJqHnR|VVu^U_VwJSu4WyF`E}IT]UcD^xYbWcuYjZsOn^Ym[x[q8xZbc+d-e9DqDQWMt~}^Ldo>ұRxpWǎTb~eò?蓒zimau[i^\ccf8l?IysNsH[n+|CR;LHBY5wjq>pIu6m<^~lgwUa|®~4qFIFMP!z,y3LvX?a)GS>[Ie](yUStF}L~]`4D_siu+}[|Z۠]DpJWo9soChu^b?|8fzl/STEf@rvOt;Xu2T>m_Zaw)Ee4PDff-cB\fjGWX{Cqa[]QjhLs'tHȳ}f6lD]iKeX/TtuK^uҳT\ZTg3[|D2]`,ww^KcDnzCyggx7ZfUkZRx1VsAHYDdaVR.eb2jluH\@GFm@Dn5tBQ=lL.V~haρD`y8cEjLd5gpB٨YzhnMrE_t{\BY[s|g|[aqxb|v~YmTxj^yr~i^}ltZoMQYy7{is?h9}PUHhDPUcRriJTH}TymTnOajIyRceQkO]Յzd}ViFg~MyPb~FqCwczELm<{MYrUpXq\yQz`{Lg>D]0\wCUw=Sxg]vPg}ErLs]Z[{MrImWu`sAeT|^WXTbzɒshnKGkoG~lVdSy8a=ЪcӵrƈjɳcpŰ^h|£ﳷާ㰀ïokl}҄ڐ~jsyVnG\dv[zg[wNL[yiԍ圭ݚi|ain`bYqmVdGrOd[΀x]yYwUlbkcxI_ukŀٳ|kK\?WuNMq3S~?c'Bn?eTkVVqcԼoχxQ_`s͇̀֓ycQg}Us{pMsOnEt{D{QfPxfFmX?TwNzc,j@r=zz[[{x1ϩAUȥhdJgdz,mhyfoļaO[jV`M.X_2N_%?h3GX"PY)om)jz)auDWfnsz5䘘ytUqICg}bwiTo9Es1@a6`"LV=z+AYRZz_ypdn@LtsYx5`mv>QɉIz{tkV~9p8n@a[G\|Tkh5TreNMuDUk*r_MT|copHn5^hkmcJhZ٦x`f]\_2[sput1OuBbj)Wp._;gn;gxB~jdXz0]o>P\$v@}M>QcLci={F˛wvܮx=t@Dfsr善pJjkWTJieh@|tkh^+dR;sRF_*yr|y]JuB[i@np8s*NrѧMuFh={m$Tjog_Lc=j]6iUJv~vodTjsmgh,Fi5yp!yc+f-iz0p2fvEvKkg~nedrR{a[7{~unh5Jdw;|4V>`Bu-jsPJhPyn>Sq]~`K;}Z~|{qjRn{aidi@|OqKY`BdiGKi?Uh*rsMgRe5jUat7sSlm5J~mK[FVxYœ?^f{ZpDkrM}X[Xgh7O~F{15Xixy@\G3eOujfHŌl:~vyLEVdAqi_yf\vFFV]})z_yfT3g\oSdCknGb}B}Vrcr~_kGy^]Qb]jNr'TNhp8gy7uBTxbo_Wg>j2`p9jk?w5Q{Xll{Tr?nO}LJXjcg`mzqj9UsCs^B`AkepATwlZMjCWitQã=ۯodoxDEfrnlgPos`aFUMzPvFpTQǸWuQvy3yO_cG`T7blO>=hdnc[ma]Nwv6ibWN"]'P;ju=_5[y8yRgwSs{(~3wT}Xs@DfXj0XxQ_p>9iAsGXR8]_8ce7lh:Z|'_=kiL?g|dg?dJ3tn,{Hqe^xR^sSW2X]0M{ۅmAڔcx叜iTűM骟^GCTGߎPGjgKp0d6t[lZdiD^jufsm<2BەxnrIWg+ztDdlJ:PoJ``^T6lLgYj]zcMhn;jl:ch.\IKũ]yplfAaMU|yKp=Zt1i}4`t0tKtPlSv`As~MfSwXsSGj<_z:|gCeFevhx1stlBInpVp3Qt.CmHypM}ŠUc_kǍWQ~Vk=ayLcz8iHY`}>M^`rZMeN~q\{_~vQPPThAfnAh|?pQwbSWj;vporucgmmItacYhVckYMeuDSy>jfd?gS`yVryU|z\`IbՌ|ha9eyDXao?b}AwgZRraZy]x}t}U`E[eLx_czsrNhlpedO]}TeqUnMhVpSmtvfpsj{䗪Vx_|懡m}GXWux_hpS}SspLYR}Xm~eIN{LsjWjp}ύP`zC]jĮӅĢґi}fx]gV㰜ijqu銜ytsazk}oSm[}oÄ|dcAWfYpXnhm|^wXuM\|NyEcSmvzk[Np]u]ygGmJqVsHnIlw]d|Q`zVO|QJdIcb5bz5ezRcsPAPbPJ\p-\sEkgGXn4LoCO^BQc2_k6_r5L@VmQb_>fp9qr?t>bTdxZ^KiuUOzYYmWRf7KnHOe?M_,V]9jd.|>iIVOqkgl|^RQZY|uk,WHp;MLdmu\rqnSt>HtHy|G|:NfPwVMSr[KJjnvaedzt|hAoGm`Q`r8]h1`vKr7Ϧa`nxmNfwbptNhmo_Ks=[dEcWy3>`q_d^]qwLRp<\^{fd?y.YARi)IV#`+ʡFgxmtTEz~パf~H,hSE&o"6@cW}SoPmgVs;s]cgCˬ痨bgFuVݞ^Tbq4t}3-}Up/`XS@Ap2S$XmX/5Dle_Kq=vR0kuC?bDtRvmJb`Yj-mDlNF^6qP/V#y?Ë`cly2]sfz-_rIDwTFīUwtT^Soe2|HJr^QtK{i8{Rڂɞ~WsiO{;uQ{ɘt]p7RjudV`Ap4`e`|eVN4iRTa=lejWٺϳ_ʂQGw1ák~Ҟw;WUce~S;GdbSa|9Fv7AB1]Ug|GnT~VkZa:^oEm}qlWAVvAҝqz>nLoIMi|K[p/{CuE•nxG|jrUMz]y]mhz.s{\j}TpqdЁ\VUl/js5^h+e%|Nw^eӑox5uCnW~y7NWL\Ws{Ly@\zOv~EATЂzffmltU|Ibot{eEnbp=nRmEA^/pFiJuX_yMkZpfxb{]fuzTxPzeXщϾzwIAuxկՙJm@dVc|[_Md{HkUgxoritEJc|t!KX`\5wX2E\ۃpejxFjN=Z|q{HWہ{|B=ʋX_LۃljZYq0_{?Rm'w+?tM[Q~RXJ]}-vxEqy~z|K9Vjc"q6TYpVUzOrAoq[}í˕Ӝ>^3Vff~HmREdPm;ٻsnhT<`x-,鈎ҀOe@~;kfI7ì3zO^jNon1M`|Ͽ󟰰ȸyBzy黴S֣M|Ds\pcd9\8d^,el76ddVAy6@Ejez[oUqqXWfcXxK[yt|pWͪWCxDXζouZ{qBUkjI_]}dhXjHxpDPI_h`dbbMj{NvU=~FSNiRzpkGLd^msLuvՅڢizDhvLs\V}w˄tz|JhE`ngkZzHeOfRpXs]{eh(x7lofxR_xC_zCzusuT}doIjzBgxEwUkalDvS}Tdg_ecanf^xexvgFUsrsDlq]tc<\DpgvąЄĜ|tepY쉥u\}g^pi]fl}ndxt]xXZeujy]cMW}Miv}PVsKbnW`TaFhNshkqpcgkoɽ}ٱˀo|xjm`jSpl~|ycZlXppcxYpli^TR`fx\x_xa|Kd|TvXmX0|v:kNY`WsVhjIjpEyJzSqhg`f|Tb{bWn_Iq\R[Dn]-o~:a~APLOfPWd8qg4LEWl]RuFwcTx2ZXbrVkpA`wBZmI_{EclEmZ=lx?U{E_gX]xAVr>jo;i|IqzNgNoF_>juOwx@tkjYӭs\w4Sh?X5yS0]|n*DO4P`+odY^vKqyvOkxBri|FXEZ?\HX}s\yzvZzkp^}x@0HXh:Rl,sBU]IuwoxeYhvd}{RGg{=b}C\RozE|mi|kxhOtK\j6fjVZhrIEX7xUZstgcr]l^ueuEw\dRqstMjTc9gy9gNcqgoexYa~HJrE]{EJ/:RB_tGlUUnCVznиGl^c?Va8[{=aCGhPh؅ocw]mwz`hWnbL|Gd{Tl\}q^ZmoMdWa||}sjSkcgT_MXu`~bChIcLs`I\VgzZtwxp⥥Ř▖Wpc~qWvNsZ_i~cpkbhehz[v]oP[Laxrxl{kVjZnQoVnHeLfWnj3}tGdRQdMjSRdAmb8pBupz}gqUkJu`]ZKRoCnjDj7pDuoWVTWfJjk_FLnG]Tb_6ot5oJXzT`[Nd]0Ur?R^JaQ@Pk3Ml@R^5Tm8sb<|GoLtHhIm=qLQZ[Ph0|6kBb2jT-fpUa=v=Ͷ@܏tmXz9u^{:{1`Jp;=YJbaCtHx`Q6bʂlĢYuTzQ\|_3^+_Ir7v]xQvqCs=|bx֌SRvilfiM5z{o}T8[v>u]H8JPy^'HIgs.VpNpQtr}M¡f:Rhi2W1ljgIk.q\`y:VNFEMNƠ6PAjÍkXaKVLP6dp1¼gʃsŗAIy斮urtEverKyS9f͒ZdC^7{-zwLxRhm9a[pA\n+Uv9bs0bKgp.~q䑔c`yeCqgdR|oSq`_wU[l)vCjPjEV{0u|EaIhV~sDn)\p7vTL>^a2`Jbw@nqȚ}iqjOP|QxUVFesG]JOhuFo[i^t=n@swUt>rOmAyjsrXaiMOujgB{KzOwhw|iqDoSxfuhgLsaZyZE\ц}wD>{fُ9eETkDmJSqA`R|UuBgJXr=_qdm:y;y@sKpV{XzXo]nSaxCjtG~|FPSe5o(e8a2R0|FFV{azXMctwleoEn>]VcO=tc:sZgrgUyyTqQo\LQhd}h_hdlf|}I{lfuqiqLGNIdFsb?8xPڕNmU@mԇOwh=XHk}Ig@eNGhY^{WvQ[h~sjg^XlyMnE=Dgvog~VaXyiƃMpoPg9|v1*?Zm]vOґX`p4ۮRw~꤮ڍnc=9VW/ku/r'oUynr[KrGdR:C~¢mҕYeo=l^7\ZV[Q.=zatUfLtBgAf4t=\Pl^T}*knIDiO\JRzx7Pf)S-][f+WyנYo_x5DyBzd=cdpDsI'A2j7%XNs6xbEƆu頡{͹ϪE{9tp%PPGxr9verJ_&HK}l`q{كĔ^s\i؝lԧӛ{hgQxReXB\ 0]$`]&Okmz|`MpRq\v_@Vq5a}MzgNFkFN_2dS\YQ]n8q~Hgp=\zEdj6hU[Ty1`f8kq`mySwu.}ejCi_Mz~F@Ygdn:\[MasnpUuB|pEe|=[y1aiU`xF|pM#dc*YAtDcp~Vr@߸ˡFf~`DhXB|f=dbwnluovamZd^p@tL]dJmQ`AHae}VaFz[~lofJZ9U[2hl?mzXXqcn|mPGdZzQqdOiGLfF=WfonsmztHrOjqX\GqH|w;t>Ne7g#h1f.J/s|@xqHSe_vWu;lhk|Zi~QH~R:[KRQ5|S*uErpckW^LMg[vfg]vx5MX]JrieDxWtjgǀZɱaxzy=SWu?.^ƃy}fGeĞrHksz~kVYhynz7jWb|[e\?wVXa.n:PzOWk[?lv8u\qsޒ[pqb2:qSRxqOG[ZjzpIv[Dw|\yty|;\_V.r2c:nf-m5ÊspFHov~n]JRUU[lW^aq9|[iSdj;r0jCDÓeځ{mEQj}mYwanXj5ZOGlGIX@re+XlD@9(J<{ubHƧmEw^AtNc@iOqNM3ApF1cb@~MbHFkrdBkrG-Y@fXdr'z`k8m:rW|R`{4igZMlzesWtS`[m|SpfRtZp}‰wPh~GrXimixXhaFljlsFn?mKjj=_C;Eb]TJh8Tl`}Rvp}ӈ]YOg5_cfoKoIbOzHlgkyZpLybr]iAxXBV|IgckvmOk]ƈK}M|mxgwxiSwQc6d|8W:IhGQzauYkIOf?XrCWp;QwGQv>hPt\pjpiy^KaEgFoTtnzbY8Vf8hy]mpv|gvpesch`m~|qcvViMgxzW\kuelixc}oag\xVx`o^m^RoT]lGymBouVA}[JZI?\39\8BP.EO2eL6nqEUc{hVB|hcshkfstBZG^xRY{;Vy=M9hc:f6oPgodQjSRm8Sf4So@HlFHhDKg7I\(JY'^a4jx7YOU}Qhb>jr8hjHJubfXYr/yIo^kvKpB{[|}AtA?g1g%j+]-sH+nr8zq4aJgTyDPlgnvbLvARhQ_mDms@oz;zBxfdidduZyF_MXfZNbHwpB|1tFy_e]ckApx?M[`qT{@x_}JuOjSs7Y^ho{zfedvw}O}guVNW[}lOi^{[^xp6eˢ`zW>eΠޥdfCϝn|S`~Dcs:yv@_}\AloMRhdTTzGeYWzn:8rPAiD?@)E5jhǞeao3UMuFWԼc^塄uayU}LmAAaYILVx[|~quCVy]Jʌsa|bbtnœ2V6sqG鑌LRfgf6uW^hC|ZPпaӹ~ikCr{OwmKrGIgRRx]NLb_s:zzAb@T|.auEOVnVfFWt8oD_@d`mNwsoN}ll:yUmFXuIk9{;eTsGTq>Z=?]=^pDuWzuwÃhdGlH}Oa[ZyOsuYg{agkHWeQr=`AyevjsYkhHZ|BhSwesf܂ճٜ~y{Zta~ō{ؚfReMj.Hr4Ec*RtA\||{YtwvgVcTeiazOzeznuVc`Rw_sNVv;UtOwToU}AWzKhRcOgz[iSfVXgfgtz`oK]|=j_v2_v?N}@MhBJc2P_-`V%^m/ju9tK_Mh~NVnDR\AQX>dZ5oz,ClRo}SnE]L_Mj*jr0c2N-dh*\r2cmH]{Qjv@}Xcjxvz~|BMqNq]pMbOVkZQxQlFwBsXHtNeS6iu0j}DuEsGXe~jW[Oq^^hHp1Zfl}xYdcvcFyVmJxgied\Z`8f{`ptibxя{kpb[faƮb~C~Ai[Bên̄g~V{plSƓo`[n8cqKqU̙puеMYSs\8bv5zAtJy]Sw`aKb5xo.@YkIRv>x?r,WteZVog/TNocCY~pPlK}]*f_rCG{UV^̺f󥌭o~֪zy”Nyeqrmn_Ifi1b|`sy殿˦qd^vo\CɘOx;dK)Va@|4qmD_iQ-\r&-].'Z\BZS{2fPoNѠξ{ϒU|:ڭ[Ppvw:alǏFbRu{\_doueU~mZr8G{Rv;̓\xJmy>VxA`u.{9rFY₰duvoloVBTmYrFIu2k?{Ǖďneg~ByKra;rWrIwHDIj\wD`bqBXu9Wp8Un=Xswd^pJ\-hzKjIo]\;lOzD{pZi1jQauoZ~RgAga{RQs7W2Zl1K`5plAN{4`jGsCdcggɹjr\m=ol}MDfiVjLirQ¼`M{NTXaZcy^y̍nkOl_aEPi;^rdyW[ubystpfiV{Jmg҄}ڏݝwzRl=VyGDq4a<{epsgVY`~fLlDUobXwV\EH{`dolp~[\~_{WjW|]iA^u:UpFLfUazceyRc~9\nKYz=[N>Q8DN2AT4CS)BS1حܥqVmt)o|}ouWs_DnPml,zx6gv`txΖLsmMZcriG}jaf|RmwvqttU`8[z7|R}a{ZbgHY\.M|kUF]r`P|[]y|YgZGe۔p?cuW{s?ey@Op4Vs5uZ}NcYq|ZxKnlq[WPW|k|񈉰glHtP^>fJqZ‘ŅLTvDdeEXgLpEr8VvAgKeagtjKy]loYSXn=@iVrhyMnIvIbGe}FZmH]tCUk@f?fLZsJ`Le~NeZkr;NvqOSmB\|BWHZMkmsWvDf]D|c{{bdqŠ㑳VjsvXizag~E\@QrVzatHQsK]RkdiYeL]rN`{bvce\H`H9TH9M5TUVs>M{@Ua@b.6v_d'o#={P[4N,V]PfS]i9Rv;ol48CGmak`zy{ljZpuCdMmal{udpM~WNdtDjTLZBc3Į:L`RIy%MjooU5cQmUvFjSMwV[]M^|fMpH9g`@Y>{\/FYkWLuOlDuuNSp_`lSWyv>LncQmW`Nlo@9myY_m{=~3kPmwBqDeD{HfkYheEMOMsWgag@y>ShXt?r`]q8kw9;ƱTn7r?ymDKvXs==nwj5AWMu0djS~S|Kiault\|ILXQ_b;j6EpQZ`5z'DW~LY{V=v9m[LԋDzeXN1ei9iImay6Upq{bfU:`:̉R\moDPYmTm>ɦFFlUUԗ_gNџVglb֝Gf3|KO8_H|*xzTlyBegLJ^|A樨xdbLEw^}pYߊ[fxPmh6~JdYpM򎂲pzZ\_xH^|:rYS~ByI{CUw:Oz0iy@HtWaJ~^ra{na~|^Q}=u?zrA~>b@e_m_=b\|Y{O嚤ZAcۥ冺JWoakxQ|CJ7OvSwKRl1gOeHPi1Ye>p>kSz@|BO4On7amHi[jr\\BcKiljdQs=i9Tw.St4rn;Y|=y\rE^~DNȆɵu\ucviÏdrddpS]bT|q|qqvgsuXzj{VbcpS^u4[}2g5b\eqktzecpXLʫ✾z^ƁqӜqpou؅krq}l_BkFpObϦ捵p͎Li?ahmdbbRkPtWYvFNnFPsS_{KjO}ht[We{9K^7@T2OoY[Sk8KnEf^Kkf{gkh{Z}e{Oz^lh}{v}mdmquScykvQXlZadCe|GkOblxqSZ~Se}NWtWmPcOZ^GBX>oSXdElbH\LPX8[a6Dy>RaBDS3?Q1uI$_.hMHYtbx^gvJvGj]W^ZuLQ`6c['h-^iBf"n";bfJC3bpCjlMT{8ko>u2Gn`Rlc~`Sht}nTpVVJUwVzLEꎂ߭osYTR_bA]?KG,f-o䕁NFwagZ}B_DKyFke:ir=pI?\cWEY_a1m9;<6X"HS#2UGhX(`9ivCl=b@cZrna^=`=nRrxHz:naYdYmoUvEOnU}zGoDIڐZvnbhaZ}igReF^ǏVސsz}QOSqO[b}ivDgѩ┗jÛ~\~x{ht_pk\KckrDboeu\pWCa\gA]^eCtq4&K:Q}USU,jh#`4waustmBrL>pxE?bdw6fj@woLbXqwak~1ʛ]NfmG^CW}On]sqehvAVKtqWd:Tk7dh7m?@hl[8TCBiOX}MieyȈetА\mFRvGOqKa~UyN]qC^rEgo:jgkG~_nlbvPs7Te^ÌX]Jxd;݃bjڏsfao<{qNSĆdojcaVCxp<@VbyJr;TK`w6IlfQ>>1drϗAȁPFYpe:=֪>uӎ^zV}}X]eK=}`JVs݋k=׶jؓ֜unuKfA}ZycuwёKRdka]8oa[tuiu۰XdzᱴΝSC|]i_=ΠzFɃug,w҅NxÚDvyb^{xdr[s2QpO{X}2@vO|^e]d=|:MfpMQmrt@`aP\A{YsoSMāP~EcAAvJV7^r)rQM[OHf)FvrS|f/`2rZ~T_|Hb|H\QG Oj4|[{`lSJy'Re/fD{FeCdQhAUz?lw>a[Ii?uPrPnKqa_?[y=BpOfmKĨc|mEH^|7biG_yGS|3xgOe:bWvm@^FslY~~BubwoHVHxH9Z-cwBGo*Gf+L?NO/GW4]S;Qa2B^?;N1@H&gH'xo%~8dZPwf\jE\x;rpLbJamWhl?twKadqsSzKoR`d^yuojTf~UjWc{GF~PDfL;_;GJ0OY'hY/^c*c^4qd5{WL~TIs>~/^~6Tr'W/a]Ԇnpơߡieuy`Jm>p~7d;Iu7cnAcWܛGe[i~TW|r;ݙj̈[o]QJi;p\l7ip,{iK]ts3Pc;x^Vxe{0wjZw7adyVtLdr/Mt?HnHz|B}~DgCCh:杝L~˃uFUMd~[~@by^|uvZmGb8WJfAYu5p8ew=jz5czC[HtrtpvGmQeZO}Obx8WkGbmEwQzKfy>u|FbNmyn|lsR`q[KkyQhGc,eqbfzNIn5YES>jOmF|Te>iD3R9OaXj8mQ{feZVn]^`hR}x{qZxAq|tllqkEd}xZelmjwopydwScUjCz_{嘣zi[b^v\bGazNzRuu8dv@ZrUe>Yw<_qNsRifyr|]vDXtKeRkQr]xG[~F]~CbYn[oX{VqgiNcZnew`qaxb}}d`sMb^m`h]ddxfiVrToM`E_HXzGbI]xPiiZ~PjT@Q3HT04T4>F6>M+PQ+eO/ba9OkGEZDRQ:f]5rp:bO\]PfUXlEXn8^nDvvC]XofLmp=gOrrS[QTxg[lckkTvMqQhe]~[Qz^GfNE^:`R/]o4bf9^n4sdDwz:~E]yg|z^xX{d|\Y\fNr2e'h q ;awSg:[+R.jU-hOaKu{JeG~qE~N\jUkn]n=pNx{ZY]InoViXsbTuref]KohdpswG9\^v^u]ŕnLjDQfhOvhFOxvElvY]PsYl1͌lcQWSxtOdƐg҇Q}oTebv{cO~T{VV~t[҃E܌fwGCoT[uDʝcxdT[rW8A'C8__$Yi.esKmYSr4y+VwDbIe'YG`yJY{AulPTOtwnmuUkr[a9V4nJ2fb>wjrZUxZu\TMmtoamrPS|nfd‰E`MTl,s2t7M*ql.Q`pi;Kn}s{?zHMsnws=wuZTpuZVҗuR]jpE؎kٕᵯoudrIiqDǒœUvzho7yxEkYyABɼslvIms7~YeM{2q~`]rlgyӯrg`t|hUo:}rexxjdxҎquiJ|h9tyD·|`N]&{@oSВmĄiݼwŕZujs7luAQYMwGfEgkR^]wdc@mq5WJ]Hh|R`=O`tKm=ws{meŜQirTV]𳕻_g[ywY~WiJw`V}%\z6Zo5`n2{qx|Ef{6^Fnj9[i2Tes[f8Nv0fwB~?GPlzFZf3L]KLo4h|lly?_ISj5Sl.tYXoCwGiXX}5mRTl=leYoGfvZSlF]Ez{AduUQ]FKlU/:R1;I-LG,EU%Oc1PX4Vd7mkAjeFncCYo?`qHalGarS`lFuv;uxCiVvN`PeXU]h7Tn?iUAdGW}eTxWgeHqDnT[oOvWWhOVe``TwI_SlqĆ3Ubb[0YHU`N;ln|8xHXpXBWzh}V~mЙR3ߋ2rQ靋֜FyLMՎDR_lxfnTYh5q^1ޢyꁁn{yKc>w~QlSy“ϛa{ҸМF~kKnLinNwNHjQpuRslrXj/[^~^bX9iC`WFc>c~2Nv?om>c69bMsjq6U2oOqtA]b2nq_*a{;Yl4a|s;cyGdvYbs{>k4^/pCoWZ~:X6Pm1Vw8kz8}[^LW|9d2[o3]z9UZBRtAOt8@T)Rm:OmQOk,Rp6dx6`p2Ni6v?uBR|xx|ge~XYT1iLrIY{JiNvXe>\l̕ޅ`\S]|:n|1p^dxVtm^Kb{M]:g\qWAs\yyvEUzLfty`u`pDv~HTv@mWkfj|{ec}Sk;jyHozujAA\u3S>Y^~hnKfVw]GWCpJ[Lp}cjYhYh?irm[kzMl|Epk`yn|l[tYpJ]BRxI`BTtIjIVzF]sNduViuBMhHeB\ySeL[}@YuKZ{?Tl7Eg8Mj8EkKTwm~[s`w[oHZ~GaZoctbj`kds\xor\eb|clXTFS*=Y,=F3JJ)TR'Wc5WkCZhAWg@]Z@t`wd>n7a\ZWeyPTTdkPou2dGkNQ}O['j o<_oZV6=!<#^a'AuTJX8W0kA{`8\zhxZnEHeW~}QMvmHűH瑺nsi__u˩s{ץkPpz[X__j4dwO_e8@stdmBq`TY_uEGBSZbpH|DKdmeYp̐rUeAasPkE`d;pm/^pBohhrMEzIL;cwvOɅcwF\Ihz;4]babl81ݨPjhznjdzCv4h8}u8rcxZYY{Tl䤸֖djSД~BEf\mtࡆDeNϝoYjd~=\GYq3^x=fhbARo.f9lwfq4l;zBvMb˅tznvzSb}Ry[W7bl4_mGsVlz_ar=`fLlTlIcohŢjsGcAl=}nnwehQZ;YHgD`PqnU|MtzehB`GbPe[om喙χPrDpHj[q{i[oLkxKiItEsSf}_uhSc=nJ^Shbly^y[]TfItOoWnVxgGpL]x@Kp\lmia\]|zixmej\^afSkKhz6ZrBiH^q;McD`m?XpNtGbfCZt;VsHa{L^LVkJdTiK`I[o6Ng=Tk;NnHNhUY~fjdsYsuG[yLaPY~T^T]|Ugckg|uuvclj}mmlvnYOH+RM*HQ/ET0SM-HW.HVCd`GicGb^Fqc={xHbd_kh[_FT^;UbAV]`IhH@xJb&f#j#3ajVO19H#Qs.RjT`k0zn5KwcJw{Ĕ|Zc_s^BsO]UMk2b`cbo}u`]`mr[n|iiWbiuP~sEu?Ig b`<{:dWiyhE\d[S]x>Tx7Pc/Tf*g1uEq\xrS`IiYsNx~HuK~wh9_dRUJce'XpF7hMIM0Zk4^uNUx[Um;gsAT9m`1ok8xw@j@g}m9oCxOMtdpNԅ9YŔTׂɹd_y?aw]{RT__p?Rp2dm&v2fikwWjvYyUgK|$u@=K|`Uj?w:]pZƍqtTL֣˘eXeF@REfa3Gi6^`0;m,0Žqg5smTd9n5Phi>h|v[{yynx4c)?[DNeorNg`d3exFl+?wᱤh[tQz]fim0N~Ɛ$?XP}rX}@XGcRl|uDqlqϗ蓏q7\]~\mŰw[ؑtzm}]xGrCb@I^7~GQW"~|Sh3]|kvDdt6~z5qNpWJ:bL=R)~KUZSY@`eH\h9~Y@|MQmlDsáOh]LUb0ar;tzMSf_tse͇di.pLaA}<[9Yw9ujGyFs:QeAgK~`^|9Yr1Ul7xU|^HbFle]\`ERawCu|De~M~ivaXfO_^gAs^|YuL|ZzLpEɰO`qʀw{Usc~ry‡xyWaUywIVx0Ix;ueUby]W~ToЅSqInq{ښلՃvm~eaexQl{XqoxGc}OlakqhnXpiZ|I_oF{\{SdLjBXx9^vPkZu_ymz`qCDcNeIUy?gTq^qz;SmJ[Jjs+=T6TnF~bPmOxThUeDRgAZtTeKb}MZr?\?Ux?SqAMdGWoFInMcOfiTfRtIWxIMl@RqKQtRlirlufq]oly^_bj\lN]O,gO4^V/Q^TcIuA5G&WI$\l9?j6IR1\[4~7yLqJM~IoX3ut-~z9zA;ȊB]YOkbeεhբoå_ҜʴtNjƏر껇cφD݃ؓvemwyONb[`:[uMi2İS_y;F~cnF}RMA^̗nkkU~C5wcfJ=XDv~uesl:Ɲ5cl@w+_0xs[[<|{E{Jmk䜻Ԅs>ɐVуr`LN^v);y>rNd}%j]{Jxitu`-9heeRdoRu{}O}PPi:i+Wr|2oC̞}st+ygyZƳžrILr1@S2„JާϐblKSny[Pw@CGy#nBtłlP|vMeiatWta肜\c{pQgNR}(Q{LvG9\xç|\QZ)f?tft^t}:pFd{9Kh6}bYmMDTjX>P[k*Yv(8hxI̠Nm,}PfpB`ZyusȑϓR>^:lBvTd~уlv{otujk~\ςjQm~_yTUiM[7Wo5b3upŋnXseE_n8bv9dLeERvPlzmjV^DoGyix~UbBy[|Une`~ufvomJbijpfjhoc]awh|bhRJkZapYZURq=rgӤnibmGdT}\_.cGd{_yfʤymsqgufxlxJvSZgGcNyGZ~Ql^pqoblNhYn^MlLUYvlSfPo~Ic]o^nUqUro~Db@j}IaM~sowoTgNm^x9Mn:n^gY{YktynT_CdwInOiSvZoOjGY}Q^S^N^AUoGX~VvZk\rPWp;NhFZuA[yHZqOWwW`ZaZ`|Pn~\oTccpc|VeX3d\8gj7YdAP7R19WD"^>3NT/[Q8_q3LsAkY@u5^>N~KLf@VZ>J_AJc9C_7UN6Q^-Nh:Wf<_qA`sDb}ForLj}CjTkPeTmqStvHuZkXtZwHuUY}NkxVnD^!e"r"9fgOA'76Y,BuBPR(n_'L;5dHWF;ox<~Ca_bwR?rBTLASa$:Z.^P'Xt7Iu?[iFOxCRpLPs5SrBap.\;x|:ebrRV7NjKqj,T`i}_7_NAw>_]0jv?y_avxDgSPCojNd>Y_$[x7[xPJw>Ue5hp1{.FelTcks \L~K~b@pTvºg~prdvNтXxzMTPF꣐dJ_I{x|hec}feoBː\|~y8{b<|`v}~`zNVvRfHMlJe|9awFcLRSTm;XhzKzQ{7O怏@Xr:]y>=Q[.ڃkvꓖ{uUecdck~djxj}Q?Jd3mwbWKlB[z]dnkq}P\l9ȧ|Whm]}s]v1Yd3Vx/pAgQ\f^rWF{upPX~DZzQV:hYDUsmAQNZtQXfCX^A\l;{kBhvMqWNkxfRvid_?e*\!co!;b[I=&8<KZ%@`JTM0e_$:ARWEcl,hAq@2K)JD$7)L&*7W'1S3jG$k2AFW_6Pq*MuBToAZr:zu-MUAUx^g{I+KE?7cn\cj_~XiA`P~EnmF}eW5{1QrtӒtod]eVnFz8h_NwY_iT`mWPO_df:p=hHQOVwKnuGwALsmQbR?gwjbq=`;dMU|,|Q}~K>Oah|iy-^Ko,LvZgN>Īι~[gI?b(]oA>T4QEv.dJpPv}LmĞXt[faW{fSfV}7KRzˆOXi::\~R}p}z@^i48c{Wl7kLL]4Ƙڸ}qQ|GuB{NmvM}[cJSpXqO[<䑫ŁmywSkCvp~fswj`.W;UoB_Iu\bNfuTfG\:gH]yRsesShIIgGgOrIzIgU/gSZ|guowvgj{gΊ⍤[TwOwlupVShHtbufzaxaY~Y[VzJAV@]fTXEh\qyᄬחOyi|yhhv㈶mX~_}SvKN}JkYcHaHVCYTt\us~CirQ|HpOo_k|q[yU`|PWnLhXocw[\[g`c?RrQWzag=N\3Nf(Bb@fo;NhBQeWjS]{H]s;NsNZXrZeg^tdkeBdh^O5UUGDO7JK2_U1]m[[4yl/m7~GJPXl^^U4Z_4[h8bi8jC|sP|YZlz㖂vEc'bd$p9fWC8>Xfx.JISW5ej(rXtHYo9ct(m~9EEMd:a[(]lFf}G=ZI\|8zW,rEjNo7vnË<{zR.s_[XћkXqTg;zPdE{kge͛E曎va|jc*d:p5w/ΛCtqltksF^nwUkNg|HVpEXt9MwFc[biomz~y^lsMlNU{Ynbsw^^zDl{V[vbj~yyZUO\TSiOlZciptJ[wDZs>sr+[E8sN5O;DOR_&Pi4Rg;u_?hZ#sFzPS^Wm;YqOwu5dwvvZd5rNRF2he'Src_`\y_ko[ctyiGcMR=|^;c]n}Be1L`]s;no[f[O[KlkyP4Fy4_PIM:Kvatyngmtm]uˇͭngNJd۝^f`qr`SCvf\lF7D~Wj:2Bhgi@tL?U5gY\n+p*EFiO`KW}CM_MocgfO{nJaPڧeqr1DYȌٍPVfixRmLIǺifn@vOyWAc`7XNbX.NWbkS`l@}ewreU>ӓOy{ĕя}TXpk]cɹuUj~ZSvZhT^dMD}hz^KdIfDXaJ{mzY޻䜘t,u6~Oo{pDR|ox|8u(NpYLVA>u5t7T{HvP[H\`PkDbHKrm뤇ŠtٕwֲiGWd΂Q|=sP}`̼Wafaնxr{`]=zheT{‡sdXZ7b3g_xw~i{VUdj~빾XwWNP,V_'Gf%nlwUy{JMcgIypWhu=lUb]OWKbl~wjRp;Ql3Sn9]BaIs[r_eUWnax^PeEi[|v_LuLeSRKwUdolWYRuxbl`vqiQbkeIewKTzFYEaK\}E_6\B]exy_tUJ_>]@Jl;iZx]Xbq8fMairKTxGXvHYLW|?^xcbdiKlYvdUoBlyƍȌ]w[Tua}T{GKcN_jo|ZfL]pARkF\pZdAW3mMzbZiWhpW\rRa-\!d k$;ueE>dF"ho6rBSrmdkMzT{XqdyL_f|XGpP\bZ^LvRh[6\9>HF:_-yk~zMuIXXSL,lh0bvqY?.~J1p`G}k6Oj<_L+i0y~=xwO~}[p?pS[]ݑpӁn˴ۧSči_ge_dw`fMf:C?L@Et{ndW2a8TaJgUd+Na_mpuv@pHBY\4qzHE^{WDr}Qhdp~i݅yƖ~=卲kavmOp?SntptkVF{Y^mS_-`E9ijGeiVsxAqz]kbjP{ikstEeex}m{Xi؈wDg`sPtFUYs7h4l<_rXoULs&YIojssmyXb9q[nBd=qFzapHqRyExM~UsONh>K`2Rs*zMc}-b?txr^xRuye~ZgJGdJiol`I]hS]qDY@kUmZ`5lM_~Nzvr\OQ|CpQYbMhaW1Tz?e}LQtt@cMgOtVte[5Us3[o9V|.On+Ae%Gc6MoAJe=Rm;ZrHBe/cPcSaE_sCTpCLc5Nn2SoBhh]w9|{UgoXig`rD~Vr`mOZqMahP_q`qFoNo]tpi|Mj~Zyo^0Ji>R{B[EdIg>hO[su_mq^f1Ad7rT0Ux)Ky=iIhn?}Hg@\NbN[|RaPnTrPSu2>_@XjFYR}UyOizV`rVhX`@fmworPhOeSmXg`qAfxIj[\rTYnEVsC^nBXt[HqS^yJjYn]x~wmFWoH^y;MkFKjW[uY`SXCWjkAIqd}g}bpel~JjBPniwvtp?hLNRuj[chsGet5_myUgo\yU}V:1OXdQH~;zge;Fz~e|EuG|T}}@rID7yxEiSmr4W9l?x?NvzBvoBtvALsZf|PCJʬht[vjLgQ́EUr\eU|\ai`quDq3>Eua"K96Z#bC'>bPY?N{+lh$;2tKp;#FLku}pQCY,jH0K\}]n#Xw-Z<}r5iX}z\aq6m0Xetڭr~Nh{GZZJdDwPOʋcɇjx\@{wQOCوkpPBJ,kq8XdAazMeE~]wUO~:ARKptYiwR[[PgVFh6Jg7b{h~otduXSNihmqzKR~0Us-XrAOx4Cm&Aa,IiAXg:]tLmz5Rs2fIV?Ls1<\(CW/DM0Xu2mUujhUjVq^kGZ9YqBxDaJqNrD|`hUtJtIrGs<^k:OmNb~IPzOo\UAvJ_MPl.Fk4`r6R~>uN^~KohkrZ}u~na6]?iHl@lG|NIf<_oGaUoPhFX}^jqfvV^v3OiEbF}Vz\wKl:Vg2E[Kj:YrATtflgfehKS~>PqFcyJaMnVbxJ]lO|\i\kR|J]wRo|RipBSkGTxVzSVzeegt\my@MoQbI[v5AW?YuWox}QZLT[{hRk=tYklfTWX~c]bnnCt{GzXN}JS}axvmL~Stcw^Iu`_obw`muBo>AxTrlmi^]krJSl>^hCvi:P]ldhsigqg@ll=`yC^iE`o7Z*b'[!` b#h#|5f]Z5>MU)_RQ:!Ld\sIey6L~e||cwkaqkUn9M[8wcMI9LS(TS/ce)]vAax@xo2yA^V[qE\^Chm:b|6zVnghVtyHXzD{i7oBkMebQK@wGdPlGZSrĘXdӃoqEuJ|ؓfa^w}kzFbbcLhVPmuIOR2ocHzBksKtєtzJktI|W38Kcύ}t\tL{fAOffmBM즘Cx|笴،볫ny_~9Mݺj6O/V^wtY~ub;tnuIvh|䛙ąyCU~2Gp/owQ|Vjbxg[D|CMwUn]hdbqy“v`ZPmbyndS׍^^9V״ÝuNah*{يzLlYmysӪȩeyP\u;iDlu=c_oEf|9_tNyo}?gu`fwWxmjAtfX^m[d3x`I|MW\4ԢȰhu}5|^qJez8WyDFHjJn}2pu>|nYQ`*{I~sdkw:_McIqm{j{craGnos?}Bhpe<`hMkiZgyEM_vK{{x[7?Y7cVz{ug^ZvWzi~]Kf4>a%Gf'DcCvm}e}e~REeO^sPQrPh|WdFVJ[{FeCeC{CgJpO^}9wW_CSnG@d5Ze;l}Fe;UFOqETvCa|Ts]_M`>byEtUaNhnElX{IiVnJ^u@lY~H{VewFc|B{S\f.W#_!c&g$x1cZ~d5_NLi\.bOQ;0][;pG_Q.i0xVzSqkaL|hsFKMT*e^%t&wNtX}ApTsXdOlArKjTxBlSsapGSw?NRgbI^7]K~rLi"4M{j}dRoBywHcOmQXd~hdsKKZsa{[jtItLeWcDO^Zq|ǘ0XXtLcKqt\c=]@_D]EfLgh4m\f=UtBw_npK`u@iXdsJ\XvEUu7qhpTZJG{PaUei`BV|5Hk9Mn8Oy;pIlF_Hj}<[w8avKmMW~WOs}:^nMWxYxWcz|O^n?SkEd}TuVeHrNb`tbt_d[}Ho3Us;X^{YlUYdSYuZIr}NpS]wL_uGa[wqxx_zstgNh}FeyQvmg|w2JQ,?O@dkDlyD^}Y`\jK`IexJRtGUdkkVixLI`Ba]CM8OU:d`3dw7`UiU~XacPXtjFiHMeYjUOm@\aHTd2WO9_c>[>YmSpeHYi;Jg=Q]5P_/f`:zGK`jhcctSCjYbsVhrKhoA}YL^azQTdZ|Y6w6|Jo8i.X_ _!d&rt5M^QK\3IV.DG='^.77'5H+],Q@ shrLhXh׊~wVkČĂLPfOzm(0\qdjbN^Ug=rn8Pvdtfd`x]}Tuxl^k@e3KbHY^fB#d;p˜ӝpn]]jxUYbrۜגtfOjmhguuxB|A}L\w~{lQ|z~}1^UW{LfXdcjTY_6a@exDtyIɇ>v}WǍVP~œquEwy`UDSbwQo?`S)=rbnb1aёo_űUiHyfXnw2aM3t9PK>jh_mz;qTSO~b;wJ\_61Qs/G;k^YHZXM]yb}ٟmr{Ffx=^;m6wYqQ͉ZusyzTytczqpR6,=xm:xU:j/[fgs:PcS~gW|NHV:OnKbe?KbANoouWmxneWTyFdH}FkyZsDxeMa~tmoxVycpkw_~lxώ\QTo~G{wxb2F쒩V]֪q}`Π̚JPdk_HlMdefF=U :S1C_'\9WsoJJ8MXCRy|PHu&y_AvGvrvY@(n^FlyDqHibtVgoaV9nm[?SR=W)BX+zSJeQЃ|fQ܄뛉`|MT}GVb{LeVdVWqhsxlmUogjMTC^qRJh@k|Ep[fnKMp/OhKnEb;mMVh<>^6J\Gg,B^%B[,Uo.Ee+=e+JmAHf)AY1=Y(B\Q`@aFj@ahL_Xr^{xƇzyQ\Si;Hj.sbEp^Ǽmh^w^dz~Zupp{azMgBR@dKfsSiIU|EtdpHSi9\l?pzSfu9Uv3ZpEE\oFezPhKjvGkzOeu\fW{bHVW~RypBi}Hfq6RiGY|FkE`\|~\m;QeC]G^vM\{nhyvraAbxS]x[U{WMkqJdy\ofsiyRMl]uarusv\iV_|g}sZJfnKX}YVtbe}WqSki3wHuZap_Up\p\ZZFUKWJiM6e|9]TjwPazK~xO~zJOgG^T_W4e^&e|>8aN>Yjתy\kCS>1__q_j?\yCWD\t8q>\L;faN}*dayhAtgqm|W^f8L܌ɂGbOʭri`7Hĸƺew|/{P/XAc(ni\w~KrOfeӒ~L[^L3{nvxtVyauAEuhCsJLmqR_zRYr~seqAT}Z^]QrumExhVxoGj+8?v@V̐Lj*|9rX݋_J~{ZآWq|6plP?b0gLUPM_:_~BtIx:Lw|P|Le5yloXe.[c3zPMdvauOokFes2Of7d:N^GzOZs/G]3_XkT]vHep+=R/Ed%NoCwKb9>b3@J4L\0G]2CY%@]$?a*;^Em-Nx?]~b^k;8U'Ka4lv}^mQpDgUGZlgqzhoxxawa|Nz]^tU~w͌ejnhmqPV}5k4B`pMoKiyqpqsmxeof>GQDpAg|;]yjfum^wYydjcaZ|\rb{aCR}hqrgMg~HHlks|anOsWn[jhzVhxKc{Nkg5nvAs_dcgO_{āa_jGrUQTCkT-`}8P|RY_G]d4ehDxo>jPatYak>jn.WGV|Ijk;c|={VQoYTwGu`ttNizKm~N|~M]NX}^_rCfo:vuJ/DY(_2t?|8JedLatg~bVl[^\Й_xvfϟZyoVu{gUOecGEqUK̋~mKx|yqyXD{fjagXNyAcVnRw{`ceaw7xk<@rjxvav@sVAsme`x`lhdnX|LI˙lXUqv%lI;Dk_oNvZmcp^T\5S].r5gvcXAm6`Dk]^HmPOT>y%ӫpʉ~̒wlm?XؼdgJV{iW[co6W[^gfyIy=gIpSwzwx|uauPZzBbcbzt}X]g+CU!K_6YsJvIjAl|EZq4TvFXxHYlGh}RZ~tgw]vFytBjl2eh=jzMugplZilhzih5MTJhPbHpmfrwjSsm}]}ek`h\gaxzmr\yxxpxaD_t2EhZcinXdQljvuivPQbCXrUVpNdtJ~K}VyrY\rud{]dIrHgY;wv3b@WxTgjIaq?]xDfpAblKiuKXxEe{F[>\qBlt8nBocoed^jhpwMuK|Jy|TwZ|R_PF|eMX:cW5nl:~GRqspHcUlp?mt6yETbFP&ks(^72{6$K1#.U*gdVh*5p:DO,Ca0D[/DBu?1hhsC_6XEgJ -\Vlal[D^it`Evvi]iCYp@efEj}I|HoDnNxIdLbyBG}N]TP>VeWkYx5~Fve^P|IdSyjW]ZbZ}˔ȍs~ҜoxҖogmvCd::rfruіSJNg5ph`~VQQ8xe8_=є6ghqyrvver@g:kXlXjH{WvipR{ah.]wdn`KTnUqߋc?}d{Pkl\NؓMtn_]fmЊjw;;uKyAwA]{_lNaQ6c|{if¨kckg:_|ٲ\CSMJqQwoWvYEYpFuれLȱw·v|Xҁ>[y}BTZ1px48{s:rpsRÒ\y;clzxbqRZYOF̉nbk6||KYu>Ǎϋ~2qteTz8VZa~~=ڡvv;zaǾtD.lr{UzNjqxreS~Põ`L|SoHlv9^p?sy=kpcqHpVqKPgy5Sf4v_LjᆖgZASgDhF`x5[y@V_EE_)TqFY~DpWtXST_Q[3Oh:Bh0Tz@Nz9Ih9iIs`Gh]upb]f\nV_xTNOQqFoZ4}4lQjgi{VzQjedicoJoHzuALyPPDWeai~n\Pl}UrrCy{PMlyOeSe}QauGBqMJ\?ga.Wn1|\7vIsjb~Uy]Zp;i@FVO2jq0jEOK1vR/P/k9k(cf>RCM|JLr6Al6MX&]X'Tr,Dk;N\8{lNPKa)OOB/(`Q9n]7Y@A\I5T3t<z(ssjrv8fgjqs͌sO|qzwkwLONbpU|Rfnd^sZ9[j|xhvQJBhj~Pgn^WRer=r|GK]fHZyfT~Gfyylqp{T˅ox]tcVHw`gqtR|mVe\Mp̄\xa}[{4ck1UJ\TWjkqlqpT|LixxRcZvfJyRU[~hlL̩EsLW\Xf:Qi;]h/Ub;Yf4Lpg}T9{LcpHavkKT~shzL}H֋{|Vw8՘RiYp|Hj޶\zbubxjd?pB=pweXru}bs?ǁU҅r~UnĆ}@O۴u}MCrнvVrBA]Ձh|߮nτEϸmTOy~^{aDɪOngYzڍBjFl٧]bɈ󮃍Gަ]uxjtQWKTcB}mzs`Kfz^b4B9"qYmj9}BI}DȂEv棥~ote|~i{Hsn5_Rzd>sPAuW|>N`JGBIK ]V,~pHYQ<@[뫷g)QVN{FiOhd5u-kw/ʁ鰇Jd`9e]-6h.J p~ZxvSfv8`ёmkYp\iF}payvbIzvLewN`s5R[PVX[sV|Pڐ횘w]NGb5\b:U_IQzAQ^,>T!NZ4Vs7XrsaqSMm@Qe6Jd0Nn:^vJq`c=hcBd7eB\rDQp-Lb;i_X8Sz6u\َqEUf2@]*Uz;fTd]jT{analwaOPw2Tp8pyPXsFxg\}DXt6Ln2cB]{Z^[AMs^vw~ph~G:W.>W=RgI_eRk_ymax9ruOwV{t@jc5alSovLtwF]jrhwWklhwKe}dzljw`yb[Un^Pp>qrGjvSzFvtGkoS`oQY{eqgjzcswx[fyPe[erskSsOepptTgQriz_zNLdLd{DZN]q9Tf&4C.9O=UxM\PY}ak[hoo|QnV_^Wm>Jj_nyRUCpVM^<\b6Py=Pm5zc(5r[lhecz]zbw[y]gjOuk:xIvU{XyExY`g`l\[|JxxIqv@j{IkxPhKjwD_Omw?pAjy?{H^>wmCz@wZlu|{Zy_wjYHmEKYfV>Tb0zf=F4rc dDd7MBm.ęa~ͤh}eCyZ?{GPxz[{E~Ъ\Վ`L܂FV}͍މ]ijT|i)vZxOrTjCG}qfgWw%#owr^O^uŅ|ۓsS]ס`ȓV^MluNitETsxࠥ✪𠻻~aRzm{PsiʣXUy}hoGKlHsDxnNWHuh9`kx?~lotwQIߺ{BpbD|3Kv`o]ץƐ[wMpܞ_|DfAfqfØudJx\V&Mt4Tw.]0[+Vo-M\9|vbpNm^pPOn:av?krLS^"Td6Kp}srQubUv@MxOulrJib^xVRuihwERn31G"Smd>RwIr[j[c~]XtJn^fIB_>QkVl\[?Lq73P,ScQXngu_]hQnoXteAP[7CX5J_:Sns_wYYp^rV_bsixXiyKkMOxIiX`b\w]SlwRuXj}Mi}BcyNfF[P^Yc\rlvxbr[y~@tcerٗЃ\dIRpSsYi}K^}X}oXdq4>a?QtCjJeq8HW+>^5N|Qr]sqxbvRfyXbuvq^gQc^X`Klh5m@pYdg8Q6_NGP+Ne&Oc)Ui-mb(2Z}fulxgWbdflRiR]YDhi8bj6vc=tHqPnUObLmVOedWpTqVt=G{JCMu~OQQjcCr/Ndv=cabiV>doCYy:o4s<\rF}qQQW|eAwMpubko~Lv>}DhsopS}ޯҎۦw_ǻv؟oao^Ts~騡i]zFs~pV?uj8axUlC^Z]gyn9sKBXLejD=bвUXĢisj`jHqz|I[x[X7xGyP\E<|eZ?s__k^|QJ猯toŽoXvCe^WnС|羜~ޑW޺֍ly׀Jz\guGa6ge\[i>m9~QФ}^h,{=bcXQrrCBz|ZjDTwPلKɘ۵P|S֔ZÜ˹ʉOWf(y‹;mJ~[q:xLqE[zjI`0cNdQzSfbhjS|*c:QĜn}0itNpYW׌KZi7b^9c9yPwIHrY\l+VvzW\8}j8Vb)X<@?q^m{~Jf{DթޛTMV;B<6iWUmb{zW{Va}_Ubb-mxHvgresv[|vvtLqS|jLwoHnYosX`IvkZpOkcQI૫݄]X6Q?hVQWs.}joTҹuRxnmxLH~.L_>\T6jO`e?r7TWkO_pOUhBVs2jI{GCquvlRsUZyd{{fhxno_ofwNZ_w0@^-JX6hNa\RdΆWc}`_gJf{Ɇ{\gE_SpKbLdLsQbMgEMw=EZ0DZ7Hf4;K1E[:TZM`|TbLdBQ~O^[xbxlVU{DerQ}M{OMx5ObG\n7MfBE^?Y|YuzWh[kD^Ekardu`qSqQ{R_hpEn|CdI^Uc`}]uUdxXPRhPxg\zgz̢~cqCVw]nNWk8PsP_ffEB]7iZjbPhy?a~Nz_d}[omJ_pJ]_sUzMqR}jG4nGeU]UAGYaBTi0Hr5Lm9W]3R]iy@TkAX_Cs\6{x?zeUnMskLa7]_3\k5fyF`OjvE}wQlDbYcWipNp{OexMteAAyYPyXxavPsKzOY|V{t?wDxHxOnSK~xPrTUdcefuUQyKcMys@EjyU{Jn^qUW:xD]K,k|3}P>EeՈlgVhtou`}MEVuSW]i,I|IpU;F손mwc{\~Z_vÖl_b_zIn~ɖuvf:S{lR4jjChwD`lblLwYle,IlbtXxKYhj?KR^>vaTlIS{T{qy=n_w}Joj[<{m3~բx֨u將lpwUIyNpdxo6kg`@ڷnku|Y]YxrDIYbmz@zqPZհb±hyUѯfgZoI^n}R~g~PjbxKF{~љlqhq7{.^fcw°oIb5Z0HUlrMcɓҋlޝq{Yg8``?wZWP~gyKgTxrholshjIEl;i_7Ue/Xx:b NSƻ{]ih/rc|ςvM?Fp[J|Yl`yao_nFTj1DfBrswPOfBjlakfprkkVISf(Xk@bZzXbLlOaepc`zXp\dLayKOlkXcyZjukhF^sG_Gs}=jL\VXsMZuChvDm@cFLK@V?BP.6M5YG2md'IRkd_`qAb~Ttk>f;\tHQ^D?`<`I0Wn*YyW\pSVyPZf?jh=yr5pVF`F]F\L/ad/]mIfvUbgCfzIPrGPaAeb/sz5oPeTp}Dh3t9vOua{@w@Y\|Ll[^ZBwqerK2SZwsHhVmNCqcrqH`Nu^s7U|=Zk-.ٕ̅wĀ|BmjDM|~trym{VEYz9BWXjT(b^/_=v]LX_mL`X\z3\t|ٙmy|^[d|Am۩{O}\QjH9xl?s_`wWCrlBC͝lkn]\_yi1G_{gv>`vB_w7+n~^z=k룚ml`Sgg{QjNtkʩaR5}lUkRd~SfOLGÆrˊpPfBz2R}-yb}mHuJiWJnaX[WF{C@oNh;YtK~;=nFGU$GM$Q{*5wLpwFJDQjYliŽyXz`uΩkJc~r/ﲝ̆tn:1[U[Lvza;xcPqBqNr=jb*l^誅y`kcOiwCQo7]Yj_uDdmɏio?M[AP8Ze^nLsj}K|XVrUdW{RUx;kkMxjAȈzNmMOs-Mk.Q}7S}LsYsYpYrh~aZkO^t@^v>Uk-F\*Nk5^15Q)8H2I\PtyKZn7SmA[{LLe:I`An|cMT?TrUdd}`n;HU3Kc(>\,Pf3OjG^~Wb{?]mF\TeN|Ak^pryzՇUmlsOTqGvNgNtXRzmٌtzJeYtznyQnt[vQj[jIuZ|{ivQg=SwZpcqexqZi;Gh2@]?CinkllstufvPismC5P@,@-6;(`?!]b%ArAO\JXd,PyDro?Q==UFO;!k`(Us]jlOyNyjl[stD>FaEbWswHz8Q}T\KUڂoW·f蟜HQibrd`x]wUDUy`}\R[lkpC~Y`ЎυsQsjgu.A}4OC̐QdavA{bz\v0f8WVҡ\du=p[uo\jSB_2_j=jg(Kd.JP,`VKn+m;qcG`qHXr@M׸˺ܢ܆q~a_GipYeJ?ygu[Bd`3ĕfQHD?.y}CHʝReiq`Nhs6PePUS1Na%zy5ZqoIy@V{0qwKM]MHzF_vAa|Pd;Zv2Wluw>g;yӝkϚ{}_twW^T0OaCQ|)YwCoj}cms|asHIt6?R:kXiOXlLqgpEwJpTajH|IZ~:RyHnpsT~zcUu=bZm:U5TjJ`Nl]zrXiRTZ-L^1L_\5Km6G[CnZNzZwNlcgd~Ko}g[]p~fYhl\kRh[oVo]i^tvףykOsJ_pFYoInEPuZoPWkaxgkuIbx7M|RqlzqpdgYZyI`Oabrvsy{mYFmj?bbR`ZP_OnQ\k@Qv7QrC=b>7S4:U*FN(AU61R,=E*bE!Q^+Ah@WV>ag&e;LDYfHGh;[U:U+>\ixjpinayYsTaOO}QaeE^f5do7ayMX}VSvSk]>du5cN}yClE`n`qT{n=w;Gt_r}YGPz]@ljY2.wM{XxahTde^nElu9XLioOrA{MzKibtd^{g٠߶˗lEO[cIfzA[sPaiRh7uEgNBGpf]Ez`McP{[EDYsSUQgvxأaa^Ē~}9]荰ՏϘ~U||yf^txq^lmppxihDdtZ6̙oL{p=`"a`&E2E;G"EpoefKZ)eXLVU3Sw,M[%)fuc~NnEluEjƖАQPn֕`fWrscNvkc^_tٗk=NIl^Ҵaql[}P]l-w;aJvF;ZpcoYω}ciH{Tp\Vo|Sgb`xrco]Mq;si@Uj_~qCz]zZ-h_8`PAh`<~Vj~2ntd`xpzrmyآҔZ4wc2hK{nnXLwQ|^Ā`]xtK~Vh9wo@umMvRyD_m_Y3dMln>C{VԟCZTr9nfxyEU/zsBQhpp1D!`\(Sh.ubPy_S~i}\V!c{Ed5eIUj;Fo*:R,WwHڇP|3^yJ\tZeԞk\u[Z͒pPsGY74H%hxPW3?c3b{a`d}qtnao`~\Sm;IZ7tC[k@Y`nMmTn}TZlI~_]s_zh\q|GnNr[ဢ_kYC}R]rXggtjXzMXd?kKbE`Vaskl}^Ec2dpCjzJ}B[d09I+BQ:vg^kE`wCz_]xHgakglDHk@Gl)F[aoSdraVm]uuoR|Xf~Kq^Wd{[gejm^|X~_~~e_etFK`b`xWbQhQfrk`i>BYHkJrhfdt~GGc[`egs|Řsrra~er]oZKYIpdWaC_r4UzSaH``E_n=juGsDjOkXgJjS`}Lpo:~n:Cj[w~TzBKvՙrbCOjo^cwhpEMvopUMw_ZREnDJX4_i7e<¼c稖~x}sUYhghTrMvKcsZ\ixmYrEgW_uNqDFO[~pl{WWOwZKwWq|uQykmSr~NQGkc|b^grEnAuFwOqCt_w5pykYOUr6[Gfm?Gǝ@fidFCaWb&ci7gC{GRMjt~sxIuafipRtw}g~PikNȊςe^d|zsCZeFt5Mlw˄{ztUÞêb{KMYQ~?Nhq@YHIdSeGISZF4`^b4{rxFvrĉ\ױyثoyKcIzBSYlUR]vvOzgpQx=y];~TQ:OnZiEcoiO~OskqNצhLeuM|S̚Tgo;psHd][X5u'Xw5|~BeF]t+oOzfUwk9PMSҖrr4̖ۚ^zjHzdžRTISr&uI~muzudSaSPk+V^(tm|FmdϒXk6ou^5crDX@{e5qȼSWßwإSi7Jl7_kmofXb|ZmuYÎeڀ^<]2^wG^hLyvmvG6C\+zuVdDzNTk4V}9fCbTHWjɹh~nzHPfVujhvjnv{Y4S7qXAb0wRH>h$EZ,^mNzxSpEn_{Po:_uDQ6CK(Gb]ωze8]?XXxRcyDMu1a~HPjXpwqtUwWOc-^tj\}fwOTl7Ic0Rr\gtMx_qijyDNY76KFTc?KbFr|Osw\de;LT-NcapT\rsMlb}s|Vpbh˃^reSdtZ~KZ}AMlD>g>EW5Ta1Zk3^i.SzALlKSC65C(2B8EJ=Y"h\"4w]Yb}X\gQhpbxAttpq{^d(bSiOYbtTtUǞZ֖MVӀ~鶡Waшa6|7by6bj02sZkd>}S΂yhXxB`ۖKhGtV\p[Na*~R[LwaWby0N@3^搘ޚ؏hxIǶնjsYVʧsЋƞontǃln=>S}GEyPHX@EW1@Z4RH&|b)3nXi}Zfdvel^}XSeM]kC\o8Wt?bcDyw7Jgbk^VTbcEmq1uETeom|tmcjVnQpKkQPa|vtg}_vjwpUTR``a`>l6zKlqelRpl?p<_ՇvbFbrjt~gk^ɂtiaceUFue}vxߋjɒLȣQcmRĒ_օdд]tGbA`MzFk9ZyXvs9wMxPuEwIwBixRg`N7h%eZ=}ElW)G}ԗ\hĂ\G᫒pAOjrexxOI߀ÁӼpwO]qyv?ioFBwDZVsr):ۚnŢx}RJϽĉnh]mtDزf\jixDXnpaNHw)ܩZԋʼnhC@ocxI6mon=DAs`ishxaF`xgbWuyukÃ֗חѹ~vzh}^РeٌqtAY^Tfv;TVc}Z{a^1pDnZ4ytʮDwjhᢺ蔏R碩Z^xUyTumwBZ<{N%IYБyN_4Y^ӠvѴWnPT̒Үp[⺶MZ̦ӰŖtmjzf`sКnHi(A`Lk2\s1̈΍MKjϓāʹ̐OpDgAXC|iHnރӘJ>ltf^s?܁a;v[l`[kOtPv\jRxYwRydjOfuYwԆPyevCn~JvNSdWZdWhQ\hDfl;jn9ry>hQgUj}Il?cM^uKf~HERyz~kh}Tz^mTXl:[pBXr<]h>lu@}E}M_j_qZsP|ULP]nay`IXn]|zepQl|p{Uc[Z_hsHvD[i{ShMtCIv2P_0fe2{a?[>^kD\8lFQO[`sZyV8jnpOfDXQwxMQ^~piiVHvPwdkh_|tu~ݮ`bPw?jvfNs͖}ڦa˿xla_SpXf6pKneJoTPiudjGYʺ_ʕthSncV}}CzPrJwVzby}wmKvRpQX7ho'K`nusE?ƝO~^Z_y=E_'p2qiKishfd:`<瓹DŽtdNใⳇx툾{}|uŢ[KVMr`XȤy{Je}8t7iPQaz.d7}hnqzCtGnSh~:v{x_βyLa|TmsڶBZࣣuՅQmZ2wo2`o-ȧuH^_$Gy_WЭ|Zirqrue–xoG\7tbI˳L^hQ\|VN'ai4ڡti}ufRzBuVxYrZRHP)aYB΃nUNc>]:jKf@\lX|u{QszVd.z^Аqgu^YWusc[ycZY|A\f=]3]_wqxru^ZSvrzЄŁnGaFhQxZ_qRSsSya}LavIYlev|hfenQlPyWk`\uE]~YrP]nƂDKe8?f[tx˃sZ_vXy8AdA]}:RoH[k;[qA_GI|P5cFBL3IQ+WZ4[g:bc4m{DuSzTr\o`cco\YXvz_nas^[MYrJ[l>dtBuAjAv}K[|FapKRoAhuB{CVke\SksPg~TyxOtz@toHttKWCbioozFS\D[Hm6Uc"hh)QYJr_Cn1Wc!Ba$PZ+Rb*B]6f[=k,Lllڎafy]zTds-ZO{IS:JiDan4|}hIRjH|BnisXal=bJ]_NƿsFRH}hYlXiQwOexz}sxaJITT}:kqe®V]/ISWPٔPP0/W{o]CЂfDhHp`.Y͢ۀIwRvo7Ə䛾v{bGX꼼⽨}gc8{asGlJܳݙaDs[jKw\ngjIQxK{|TW^QŠաs[tb]tzsfr\QA`=Ab+BX$5Q#QkBmSi{|yf|d`bxMF7N Sg1qPY{iòUKd6Hf7B^VѱYdؙzMOk-b6~itӀ֩zj[oWQk=E`v\e8Am>`lOZ7diګӝa|dph\CjPTv9Ol5~ObiCaorfɴ귓ɂk~Ui;dzEMp99T.Ce6^wUmC]{>]xvrp`\}GXtHxO^mqUmMEd;\f~oz[To?ewIfcˀz_ftDeDl~Suhcvkw]ovoYb~R[{qk]\pJ~R^oIwCWq:nQks{a{–ϡlnZ{KnEQuLGgUTsO`WhZz^pF^t5Io@V{V`e@UlBI][dbmmT\u6Bc>b4=`#>T(I`/DnESaupf}~Ie}@Om3_c3Zj4im6\7m~KhH[tMOwBLU8LV1lU*nv5HPE_QV^2bg/Zy>rwUqUcQtv~gyXwhtiiJYGd~Mm>vwlnLgAqzQkUvzWzuEwnDmtFnkQakDUm8]u2nt:_u=QrIH`CAl;>fA7]?=L2[R$P;X_?Z6e8MQ&h\ lJpTrYTjdfyic@szBn}=cOuDiIfPNEvian\h_GXxliZ`wHmh>?hm{SHyϙ;vkKQjQGʱR[ыJxTDlxuUtZX_VuM}ICmlunQR`Kw>Bx(Q]!f~(^{?oCUzgxaWς|fe~x^ksce^77[SLȢx[M״ߤw~IpzV{jpF_sx]nZ1nUyWgjbR1-^Xr(pOXcsJTsNWkHi6[|8h4L[}H{\Lprw@^戨fْr,cnhFs2nBb[oogc|@[c1]9RcWGNS`=\8bHȯ>?%&<25./65nHkKn{@7OAi)7<Uw4Sx7Qr/XyGWzFL[O|jmwm_8U.Y.Mz2jAlNt;Ku6U|@bHpWXrDSiB]wgTbI^m`Kkl|~ZwtUe[^|Gh_ImOy_dvyflJ^p7`s>XrW~wzgQ_lKsNpb}uqiluLol_{Tf;Zi>iTtń腨Ŕy|`yen9Ta5MoDVzLbQXQmL^}AWESpbp|ewP`F]zX~]m`}V~NHnCdwAP~XubldZzKEjQb]\`/df-`y>\tE`s;gyKgGYTFuSLd=UM1]N)`e2LmDMbLP\1`d-hl6qDhGXq[kYXgUjjQ}Kq\oly|`yHf]fzSpnB~{@f}Hi~Kb}IvwOnAn^nQwuj<g)nzXiTzMii/d>bQiImGfX`QO`^eMvZr9ibu:UfxbSMqgUC]i8o5G|ƏQ_vMWF~?Spdjfpbkn~XT]nO}cXm:>KFT1\_(tw;MvouYZKj}Eez9}KtZUO^v\s^w`.eb:z3pey|teZsgpELcKk_VVoYv-|FZaWd8yavpcwiUZ>d|Ay{Gc;V,jd#fKvIn_v[MtrqY`~jPmilpMvH]h8hFbLPȔq步w~L=?'hfY]kReXJuHONs\xVz]oӯznzydۉДOdxa|I^fHl.FCA}Ӧbl[xQ^zroZߡZj<cgPPR`[xޒUU_8MYyj{ފmWY(Qw+Kw@[wM~^CX-}TZkymffhNxQTx~lYMtlpBlCRIfaAfi1tn0Tb\i:VINpFgi2hp.Lazq`jSN_l{WqqiT_nFo3OuR}Lp\_ccJJgWtiCM{hjbUO]Hv4[gVGmw0Odc{Pu2i<5EhJk8cN|LcYm?TE~z4xT|sf`T|fo?dHypZbfxK`PxpPSxjfXʺb^`މyۥf]xaPeuGqpkuyAYrkyPeiQvUr_UCiJ?p&HZe(ZfSu3\r1Ui8mAf[bBk-OԍaNIwCjm=kЦMljuI[@WgRL{cjUyiZEwBJznYFm{uƂ;$vE4v=Sd~4Y)\j6V՚xEwxV;Pm0ewzk|~ybq|QY7b:yZB]!TOZ|a`denq\sIYlN_mBIk9QqDU^,o]2at8U~LY{Rem9cp@W|GXeLif7mk:Sx;^pNr`;gxEfcCRc9?`6pN6Sy+HbH_H-r](wa8_g=L`EWTEi\/fv7_G^b?za@r=oZckHsZUYHoM0ut;D^OXr[W_A]bDkgAPvGPfD[X2^X0N`?_V@R{?=cSM_A_a'hu1q79}Va^R`hdbaCwxCv_Z~V_j;{O7QyZUZ0*a^RnRqq<]Fr~6Hl{a児|st[zc^]3ox˛MupsGt9|?MeX[8h-\{5^8:PJ/oTJuIvCGuJz[loHuvEsPvMU9SzcÄh{JxkC{CbuPHl=~Nox?[Q_>KSCYX4ca3kl6QHWi?kj:[i:XS9Ul?5mF=O6cH"eZ'm6gVZdYhBXrDonCqxCaLkzDms9`iBiDrKYwVN[;[d-um45sXw]j_BcpRM@bmUiWZh4j&Ju[irc~DjaokiCCuPokph{Vx?Gyk_]\{@~v@pMfTy?fKspUeLN_[KtQS\:lj5st>fF_FTx:mx9`Y2o4]`aPd?qb2R9FnMlW)v3Ph]Yo>tmLp]YBq~+KGol/o=uiQEwbV\&zg-]ҌƄyVuQkdL}?w~EvWyVPxMV+uVrcUV~}3gY{GkgIyYGf(Wj28~4)8,W5bk/jyA?\DMW(UL.bj.7Qoli*MSbw&cDPxH_R2exCby9|~9VuKe=FwPU9/z1~h]~{`UMwuOxew`tYxH^N{}@]wMVwakl_BWDB&sX3wS\^i(PDNgP@ZCk=qFc;Sj;:u]CoYfSXu_DTldQz<[O>nk함?НuڍyOGJĄJT[exWuXCzPJ>Y-BL+:G-:M/c]JkuTdpp_H9J&ZtPkrD`ڊpi^|q׃obyxu~oxzwfhgles}~j1A[&:J,PZ;`}DVf=X^6eNn^}鄏dmrQ{wb^`vs|‰KhJmɉYlI]xUyh|jVw;_{Q[mIWi3A[7Tm;RoTxZliq\a^jlrm`pke{KfIn\dNf_|W_~[c|Uc|Q\rYqueOsBYF_~?SjDVmSdfax~iwVjRl[ueVekJYkEZgGWqebuhraiL_jL[yLdxMxTqTBZ7OY4M_*Qa6e^?vd:{uC^^TtoSbHRb8@e3/\5:C+HGPV$WZ1Cm3UX@Ff1YW6cR)Pb/TREad7clFquCUp=f^F]@bg;[t;4gB:<3EF)GU0Fc;eT+c\*f{Q_S^wKm~9VG-lQe?`f2]r,?N2{VQCd%1)69'ZL3DP-g=ir^ǺڲnxQZig9P?e(pecyv|Rp?ݖ}seB{ehYLmd1RiJs|؈UuijmoއۂylXVzPWy2Fj:mf~XktSLZ8Z~Lytk^~StB~@\k=dz;[Fkn{Ӎׂ|ll~YYb\s{Xlcleob]w_`Kh[}|i[jEVxKNsLkke]mo~|JMn:kRgM[d|kҽ{|bZvJWx\csUvVnCePo~=YsER15^j,omJntI]tKfjJjo;nbDke:RgAOcCK`.d]2]|6]m@T\5`7w=khrTL{_T}}Ur|JexXbqDj}I|yDu>InEs}AvkJ_zGfd9sq>~vErVfgysOqlKlJeY`~VkxQYeMwTOb6b&xDFmIH7OV-Wa4mgJ{LPvMjlN~.T^mXV|P|ZeMi|@d|IptHm~MzvC|g@ol5gxR~zEp:rWuo9x9_}HxwAbCl|NxvFmBsLo=vOgqInd>h;emNQmEib<=\UsK@Jfdas{nsj|``[?|q}{`bgnppChw{x]Y]Kz8Yu;N}??Z2KC/mhBXgEMsCFQ-md;cX\zABY0P`(og>B|gQU{B[{2^s0T|?Bs:b]$tr5cv3U{7v;]wKsFebKXp8|S{WLVmg*uAPiGMc0c&Wm_jf*7ojkH{9v7sEMpdj:Yg9=J%[_=eņs¤7O:Wdn1QݪhД|}P_evG˰^U|CEyT~CȰd\jѕgfn霶pynqȔv۳ҶݨVTu-O}R_xQ}cqq^ǝjtL]?Sd0gy2sOl9㮄wqPP1ߗ^kIn=XP]ʰFc֓Sb+WP-qq^W_jy飯uqvÈ{yl֙V9@ZJl6lPOu7U?m@bC]bRjXpj{flu=or-5A0xzZTx\|_yhsXl9S|UqpNc_TUIucv|_sW`aep@U|AUrRlPYtK_]gZ{S^|=XWytqbpHat,Bl.Nm?iQjHh;Nz9Ig3kn`yqvHEtNZv[qyG^Oyo|Wqgzz{tmzWzgftBawOnXiQaPcq>VfCm|MZkrpye|buqyypph7YxPdm?bW{,<[/B\6>P?ZhDVsSay_sUi~D[mE[sILpE\UO_kv5t6xAnJn>iKfLoIyw9|qMqr@dT|Gn~Jp{Cab`}WMzOJeEIW/UX2xU(;HxTbcuW)dj6^c9cz1\nAbz<6jyx`u_fBypTNEh_UGt\X)Hu{IJ\bI@7{dr{@{LTOfwCJvhSjz@j~GtBXVb]Dg={rBCy_eG6^a9o5QrKWqHOWeeWt1Pf*MW-st(wKay8?_dSnx;GuQT>^r(p.ia|J@nQFj1'lمenzOjbaz^HO<}{>Y]ő`ddp@i_@HUp(rwcvELl~Yl}udd~I`jlK,FhUr}H~Li}<[l(~Acgqxsdl^1M"FK%D,7I"6;k]%Vm9Ld'kyG_d6ws{ė}e6`]%_TF)]W2kl7h̙r[e~`xlvuiTuoDnxJcuTb\6hvasol8~Fa~1}Vng`wV{AmiXvxLo)I`M{WTu]o_2쾾wnOM\;mp6wt\s(_\/whBpHwObbHZgءL4zstl{Iɸohg~Zx\Lp=~fAD/JJ2_qEYo1o|KHwnݤrr_Re7sy]rl{L_WJ͒|λj׋َWYzZ@d[2ym5dYH;yBdxR}vxa=4N:orp[6U*Jj6zprӝmWXpKmykuyqunlCRS#?K6w\exky[pKOuTPaN[ku~{tZiKjfwmb~Qc~cggr^iVg{EIWs9No\t>Ug2CW7MeJV|PUwESfFZwIZmQMsGXYAWd2YrC_mAW{?Kh=Pg?TpNvoD|DxQzYl^J^J^JFV5XK2Je4Q^DP_A\e8h@VHFt^?IDH<*TH)XV5N^>OW:XR3[h@`bEqi9oLssQcnRyfGc:}Jnba]{o@zGYizwnqdfUxTNvF^j2e},]wB}lCm:dI`vKmkL|>Uxlfmi[k;}b@I~]TbEzYEU5KG#J%3jiiucHQHXi0ow={H@~X^ihl_L|AMmCba+k|?Q~P]Q+kn>tS[E~YsFQXQtRmLct=g=ZwGnc7|9caiyds|6mmRn:XYdt]\wLG~\{oZz`WNxKHa>HS$p}7YOQnB_bxPxqNyMyZOGp[lOq7|t)w6EoQloNkUs=fWrBoEyqQҬLسrWnJcca\2iEh}_GwmqA~dZa1gm5ttOoBR|]uYnRUg5ng9X[6my?zEK]YPXRtb9ĤOn_iv0^qUnKjCScIb4tXvB_}Bai1|>|^s[dSBߴtp}{or~UtVUwazH[xteZI[l(Uo>Xn9Zx+_y6q@mJkLnH[V2}o7eHqr9`רkk+]]ll=tBOߤf`WK_2T_(rr5^9rh/Po7X[)lBgY5>8?*C;",Fg^1F`fs=Mm+=CBRf!rwF$Fn$DCG}gWsCw5cݣlUi7ySe탬hPa2eQ7aP{h~Zĉ[wd;me2tu=ZiuepZxk_aIJyCycu`zR|nsLvs늪ψdg;z@_wfp^]rVw_s~PLs$Me6^qRnNbs[bQYv9Ql@Gk8b`wVuk_WtYWl[`{BOe-F`CTg:Jq=XP^LbNgHsK\zY~vsbQj.6R'?^KqwbeyPV0Mu9WpHckW}ixJY}=hgjq}jq[mMYnBYuTjizlqbtNa}JiUldd^qGauN_CUr@ZzB`RtP_SjOw{BOeUpzpySdDXeQsJkUb^LfպWwFe}C_w;ZhFiUwKcsBMpPbCUkX]zG]uH_vBOSfkH\z@^nFexD`Tg{RfGd\iUhTN]ShX\U9vk3xInhq]eTt|W^WPtgPX?]O0\Z7gS6Yu>IbGLK1OR4RQ:yN/sHsnPjpSQ]@YP8h[2hiB`mLY_AxO;L;}NbkeoeRnlr:a*[~D}i5wk7iJksJnD>{ەzv|\Nisw\bFtBY\7we&}Dhn}hlapNxKOdjWFf}OUkM[pHwy;jJLnG`^ac1|w.gL}X`_z[VnucjgoxLjK[TZm>io5u8_tp~KlDr<@Zdf}hsKRʎќݦkU|Oj>j.n[g]Tڋy煌}giZ{XM`fhc5R|gObak,|9?roE]t҄SXX^w@pl6]7x;{OaoxEc\|{IUE`v4iuCWPtCpwAdDXs:et;m@tz^{Yl:NَҤxuK|SxPraUzxBvd[h|}8TfyRpX]ਫƾn{ic`r?/ˏlgi>Jiuy1ZlmYtiP]B`D}OLmnaW^pnh+weHO{h\`O`[ݒՇXWFp*NL!dd*~J[q.HDH|=Jm x]\oe ]=3>AJH hd>gXS&^^Br]/W_Cwo@SS>\a)_irSjj0^|L[W-c5ܺO~ځǘ˞gŝUdvjrgD`1ic[x=^Kc`Yدaoyl@O9`y9CH-~g1f#9c'@Y)BX%qohtqGK0mQӦoK*](I&k@Sp{NdgoΪmSmHzag{}ڨwSgE`\:eBhkݬ{FB(NU$TdLi{Ztkt]r`nNKy9oZ|∩鑥Ҋ{|QnBQs-JU;Mg17R(Uc;df0^r1VfJgZgf_y_Xh<q_lI]{FWB\NxS{[hUeUtmeee|XjD[}Nbf|fTp}SfYkJlfew_lxCfrFSyJuNfC]JdVnXzl|Te=Ny]uk{kcOmyO^\it|fy|^qSaHhQeVmWamETnTlXfHQvgUjGQ`@]b>gtOF7rH,iq?yaL^jEKOC`F6]O+kR.nV/[]:NK7wE.N-hG_KNZFP\vYSRx2^p1X.dyKbAsR}QOҟƧagyPOgufmGr_W^]I9nasD{A6YT|_`rw\jr@~:jAKYyߝpql\{cbcaU`Mls5}p0QatX{>6{Gx@zPvna?aƥѠXʝuazuQWcalDoQZ芅oy[wqalhגjsRC^isOu:WMb]h1a/vD`}umJn]j^}NtfnxKh9`e^%cr1h6\;~C\[]JT}9^x4}9ihrtHRqqyJI}n[vl`yjPޅmӊQ|jKUVX뗋¡kӊ`qd>V{hZa|[scq}N~]쒛om?p~]|EYBٛ0܄|~n|ưH^ݼuf'rVr`v7c|Abn0jr8buL|Mqw6Ied!Zc1\QPbXBqMWkhzUnzu-`j>t2tńd́_Y/*a?)\GJ׆IznL]n.Z|0C9$GF,`3`j[:Ba'Jf-IP,Sa6Y2=T!Y\$}Z{MMRSqClygtUhDx|МWTNqz}ro^Sͣ↢؇SSXw۬b{ep(bTySʉthrI֜|‹TWj6aR1U]6aA}\?oG[=*\H?vnPmoN[896{Sc}DfJl>\'1Y*'.zXʲXrBopI~\GX?GS8pVhhR8>n(W@fN]TֈٿSa,`:trU˨uX{RbrBNwlfքMopAݸy}}M'Mi7E}/Ed1Ud(Tq2`M}[~jofsIiSXH_y@SZkWq\xY}UqTmHY>Yv7AY3lRgtÈto]_{^d@Hs1avB~Wl5>f*JmLsdvVbv6=]9R~SyޢimX]Utpq`sGgSlaoi{ptX]|RqShk7G[:]{ƻ_]xEhtJqyJkSs\niMYtJf]U[n^{thm~XdxMhbup{qPhqYvӬgvWZrTt\xQ^tMdZd_e[f~b7V:-N35?1e3)el2JV\oP~p3d[XwTqJHm>Mb;ag;v-mah]u=X^tkÂpk^qѐɽd}[uZu~IwTtTЦVXcaUYwS{7bxvءckSǟ๊5}GCisEVqGeJ_ŊythFHwd~aKunx4vEizf8:Le:t>N_kz%fiQ{<{fTylVxmbuimx`W\mic4^i3b9enBTkVLTzQ^[ky{JuKrHɧE̊㵡q|Wge[Pʬ~aeHEZvKYu˺cy[\t[jE]a/Fyu}$u>UYa^&9TDpY%ʐ[ҎyvOU^b8Fxbe?jOxo3qAm>v4]}srkAzE\cE\dJU~?sh&OpOKjtJx{LDyY>hܡҗ{F/{VMǸ֓{yhlZHҋhˢOy`uJtA`tBU@J@-tY:X]&RY1;t˜[rt_nyFT~sT=il2nud_wBT1KryU4W# $,^gXПZjCfyiƽ}iL쵞ˣfXTKGNwcgCLae?xqFVAz\cB6FE7sqAl^ǢyΣrvyĎrvf}_sL]Ky?n_Oob_Xqy|xdgMrKoEW~3\9R>]z1Gb*QgB|Mhz4BZ,ToMoGSr/bn;ra}awcZzEZxK|t|U]_CTv;Tw=[GfuIdzUu__H^Dgv@n@cV`Q\}arYfbdVwtQi6Ia;Nj@Ju8Q|4UxC\vLW}:Bd0Ii?qQem肆CrfZzY^}P]^ntso\a]x_NSkBOtOky_yBUqNpT_VfqhyU|N_HXxFaKX{Hb[mN`O[{U\cvlHX_B[vtkdq|Nf|RomPXkMw{Xu~OWsQ^vORdTO\3,`0%E5B3sHV4T]bqGF@Hw\C`DAK+B[1mW-z,~C{cn`w|g]]~gy^rzSjzYnvU{tP~{JskXshTgqYsz[`tt^nJLofhWY_AiQ6JP.e;*YX&UW7dK0lp9{VceagÅLdbaI?m)7nlfWkqѰoqw]wIITe^gFngT^qMc9QcuX?p^MQ:YS5Ys/ex7VژSy`dt^a^|\}c{cqmSweQ~rgpYt}cbUX=xOFH3WlҏřEeL3p2eI[eU3Y6{7pXVMtBMf=A_-Lr*ih7T8Tv,J,Za5+`sz[EPZt̰~MYn0lK`]6\n0h-JwfwT~KyG\}pUS}RQ[{zygϝ}{}Lky_~ResrKYX^p[MNT}dN\ႚ|UkQWpw>;K^q4w0E~_MszlK[ţӟwonezYTT6bT*tKpD{?nLss\8BiRL9jt]d7guFkI@[|t:U}Hb{VP@I4D<8Lelb|mf~݌zRYNmLD\t=k;myCZu2l\;ݖeeE^rE\{=惄d^fznS0wYc|uLxQivvI݋[?D<-4xxXem>\[dzΘhNd{3٦]5UwLۋȖⅪxwSO]&dQ1r|;Lszliqu\d`Ր*FBR.I<-C=8+BqGppǤZV٧Л3s(HR<ߖr]B^~k38%xFr<`uozbԨzpj{z{EYc>gnY~hі݂뒑mnaaZco:dlP}xTX@nRww^zl]Jr.6J Ve;cq=K\4Tp5aWኧvk~K`xGC_8EVDyPoVn_Beu:a}?^Cat1W]9D[4eHUv9Lt9fWxOXxKp{AV|Zf}IdpG_~V}sl]rO]CXOvd`~FlUsZX|.C`3OqNnRkb{qhrWidltwyrjhtss|XZ|KbuTd`ulzum}>UlHaMWRqJVp;Mh[t]nJR6Su:]CeTfa~eh_kaf__uPWa5Q]@\|ӳcu}NfrAWf:XyM]k^Icu?Wu8Db?7T;8Q5CQ2SU+Zh+wn>nK^VJRXmN8e3MO/JU!S^-qd+s6pWeSf|Sln^f}j_x?fux{ptuTPycvbSv]fTPkMeeݯ§o}M[FT`b<]UK6ZO]jGaa+m~3vYfe}zU_QRHyg7dK8uwa\wkZcmWh:oUw^R@ɋnȘ>_Vb7EYLZoqPto5u1uL~\ɞNɛǝ{᫰PmwKfhŇil7Ti@TW-U\1@<4_c-k.bb=[o2Acوx߆`Ck/p=ry9s`sm2gsZ14i&2\6#e`ZVr9{lEb2yu􋖔V`^J?{{jhJhHgQc0~xHwpPhs?pBXmiZCjAWycmB~QsgNk2Lm?Re*woB_M-Vkj]>gH%o`Fށ]t?h>`A׉ckbnjo⺔UŻ}еМxߪۺiThmsIl4xPX=x}ƵψbpẀdƛzoġ|]bJ\UTWp+\I2_ڊm`oAtiZccbY&X~:Ec`J}utaUnKMT@e}MqGlOZrKa_uwe}SiXwX[m8]j9~QUn}ok[{:]j7jeApMo~JbhCurOtAx{ApGd=~PsHeJVQ`l~@ktA^f;jXs~=U[C\iY~~p|joO|aZy;W{T|USx:NvBlkxfuVqLg?V|:]Mh]l_b]ww~ymuHXqGYxAqPxPhZ|VnJSIUvIm\eM\~@bT~kZPm6S|B`OsanOb]nYe~JgzCZi3PlQ|Ԩ\fG[IStQkXbGy{H{VwK<_;JVVswisoVR~clpNȨRmؿ{tjkvkg毲|qot~xR[pPnSpn:LCR_AuBNYύnbmU]z}eRllV;Z=i}Lp{FcLrHb`_}QVuSř3hQZ\a7om2~Azph}xLbOV]cO}\j˚~Փ_zXlHRg"d`)j5r>Z|WXH0}c5y7˰R~oj?by1]5r1}HjJJlϢ|NgNc?^jTp@m>Ctˀh\epBL|L[i$9Ldem|CCKhhnAwR=[lmi⁛sQfܰ[|el5e\9sF|lNclVf9D뜈Ηd̉[v0[D-bFt|9|N?H+nBaasAʈ|ⲴaѻqZv[DSBNZ6PW#j]23i>~=l<̙^CYXH!\IJ}-ji&xocMmD +:\qK_8HT0}a8{ȿphozo[W}[tu^mpLxKs?N]?gS)VXDIYzҩe[2_-ڳo{[VlgHo=Ji"yeD%KN6mtN¡ݍdoU\-9M$0HACx{yn{rxӳo=`h7qHɅfX_9~ZwNcraIG0>]vZMs@H_NN]6Ka1MT7[Q85d7?I6UMMf'ca:sW.ta8huBWg:kbrs@~g}ZFn4XXp`*hc8}@}NdµfߝҮYd'@Ke~%dsDR]1lBvj:awKr,e0PJnjp/~qp_jXƧٝUtNtqF_oGeW6y{WtqG'FHm0țȟ‚XrhyeQNqbq>eR`H񸅹mzQk=Qe,f3rKr[WSo;ǵ\a{fhB5O[)QxDʻsݣŲ|`uA:FIj+(D1< FJ,Wq>eg/Se?izwnQm\nZ4N}etRx̎pOduAiԓqՆ{ō匈ٟӁԔޑj[qX`gKGjKa{kΗm}xn|;x9nq@]sq@p`Olmefhij`kvTorc}>]{@ad@UlIgc}~F\l3Cf*T4>^/Gb;NeBUtD_xK`aBhu\iuy]m}?ZjA^rS[i_iWss_v1GpA{vAYvCRe;FqAcwBZuXank;IRGnkkUv\oToRp`\hl_tmG]uo7M0@F4GV*Q^27b:>UE=H#4A94(N< BO&>P4IP'RY$e[5|Z8xh4|pQwrDqxMugEwh:g~EelJyt=q:KwWWW`rR]sGYh=ZrBQzLLrG[S:^%m:LeJhPX7_҆fo{OvLX=eK']r0opCVJ_pPyv3sElEXł⴩vԨțQjS"l@[e}|RU^gU}ZsdbhSZr}іgoh_V@t;QU,BS(ZV,j6sF]IRFp{0vHf~pUvgi]مZ췕qdžfurtiQ@{qs{aRo?wV+n:JcߓZ҃۾mɟPf|l]a^T]Xh?6kđhbo={~PdpqnFI=sh-CX{U^Ќ㴥Ȉ~lR{E\I~v=cvoBTυϡ[visNqQk>R/itMrOzOw{i\kc{_t~5PkQc4vGهn`Uv3U~[WAv3EqzTdSy@y]ZVHy_\gYtp:i?yk=k:n}fʦ5`wTDהzHN+8U)q^G|,{y5`t-~йfiiznIgjcgjYn:c9صxIe.apBpO?\Ikv6is=ij0nDsc`ZaQh0v7NBL.]c0mItE@iXLOfx9Y}R{kYQ7XW,a^?xt/DoȠ׈bxxrЈRNe?{IxxL^qz`eoyLn[̳{蘣؋}ͤINz\]?hTvVV/MW9}^=M盞Z襬lJ[7%(C\'myc3hWc-aW4KX$M_.gp@}}|ƊvJkCm2Yh/Y@Gofݐٙ}}ōugzԞ׆b~YdscqhlqRnQdb_ԐWqeCXrrv9yqCgmY|bwoxegDŽ皣즱KvYtVkHfBxDhwqdVeIQk=H`2LY.Pb2^j6B^'5L$Ak4Vw@Hg-H`+Ie-Gn*Jb4A6;9B:PDGT(YQ-St3Uf9bl5|h6}|BquYosRflOZZMcX:]y9ZlDf8l4pxNzpQhPDaLgW]`6ok6lLRfKi]``4eW(2c8;H>OV*ax8uCqOpaYTLlEoPTREH,k1u~hYUyZk^WoT1vȏhTAOwLl?9+DJ hO.fxK_Lg|_xw8ke^mj¥hՉFc9`.j=nwFyu>@vWYxjNV~qsY1ZkVxڍ⣰˅֑L&nf^#.M)E5CZj"_Ґj{Dt$yK:p8D_%di.XoS_~8H{0Ui4JxOr>zUKTuDfI;u}?rHNŷtyJEplLFa^\{Kyk*=F5h`ztcŊuTe}?̆fbGn|뀳Ok;wNUG(55kMDpx?mVJTvMztB|7yEoʹ6ire9Bo-J\1Og2BY.Fa6ht@RV,HU:bWbXu[‘܇pqboyoPYtETm~O@T.omHhxOXoMiZuV|YsVqOby#PJ$B`0iS7pf,`xGfwDwu?qIo\l_QsW?ZCNI.OW.WcXd=ff;V|Q>j\2D7B: W?dS.k3JifdcdaVcw_m6CbΓlpmXL]JIoZHXCH[6CW1n|;cs,NoR=c*dI:]j|naaK~Ti7LdAo7_0WRsEW~uqzPVXrw@Q]}WJn6Zn*w7~B=av˅Twwzv~xRnoAth{rUr>U{rV]IuLig=CjggNrqY]G;uucuSXQdv8c>=_dM{7wt4V^;|YMike'<ǿJڴc7,Tw`Ҹ|pnmEP{mxaqJW»S}rmAk__+|aZn]i3cu/X3]=`4~u_M]mx}td;cR8b`1uavB`n1nlPbעugqdc@l~Tdg~Wkwir`vá^Kwfe36QFV'\}qzՉ}r˄o7Xslr3q0k4h^Gm=euW^4|dQZQss}ۿF{oh埝acwI~ekuڔ}XYVkuu{pV}}W^AT\GimLPw;f@X|B}SpVz[e\{|KurgqUgSqLsGvemsuPw{_Us_t\[d{MjiMVmM^wGF^7O[7Vu9j=j\wP[y.Z`iuCPg0GR+BV#=OCH-FA![S$oo3^|MFuTF^K8`L7T@?B;QD5hT;m@Mhr{qhjXiMp{CwF_9[v66\7,7*&.` 7jLV|֝sXOqLsƗv{cfVQin::[Jbc<*Rgǀat`|}6J?P^T^Z-hr'=Wqm`hmLW^~=_UlXv;^GlzÆRiwWTi|o:oVtBzObxKs2K]%`l/;ĘAߧgsSxtYk~=l\dbgm?bvN`oElk:zz>znhkfyfwVctt⦁TmPrBsҮVuSQZ5v;`Zڇe[b^FycW83RDY!Qr1@w"'L5(.0Fn;%šdťU2V74Dr!@e#/QqN8lh]>l=l|IJz=q2RoOb.{}7EДypmrUPT)ng<[h0^{Uwة\:Y\wt]ђyavzъkWcd?J{4Cg$Gn)KQ5nts?zGTp/dL:b5a)\(o0Sw3w7g7`kxWlFUgI~F~DӖohwoh8qVKrO`a?m}[߈vsvցէ㉄dtIyrLnV}Ytcj|؄RjxQatbUbA_]c;cZ8JW2[[)__,pa1qHtS[uMThOR]AL`H^XD]k5o;A`oStLdOUQ7Qd;eV?ya5z?TfxR>eE4B6?;%cA&X2~FqՆ[y:7<29Q7#V%drlbqX}fSmzv_w^`jkI_z?h:}TupasDLWMj=HtZa?~CBySbe||K\UP?u2bboRydi^u[HI_AuΜzvY{sbaWvSq|byhY?G`2hf/wv}R_XvpA_Y=x\UicCGpvKsP7Y=a75]`5%?/!}H@O_QA L`'7d(]j>Cd[oikp*ȃvwSh|r0reCb5^d1mtR,nQ*:P&$14?\i>ԣnUWg=WAm o;Qk2p5ktA[ٰ{Ġ؁bxiԱӸacwYqֱϕȂhsKnL}`xׄ霳ߜԐʟ}`>yFv@^~Gq|RW8fb>[`8jw5p\Ldd@dhG[hiIL:[uGm`Y`Suڗ~\aw]QY`(Xk%=ZBU/HiBm^n]sp7PJpaodx`uG`3W^/TZ0Sprrg~;Qb7mqb^1_d4^b6Qe=XJ>X`67K>C<:[<3oU1yCOYuvtwmb=i8MJK< Wf&S{R[0o#~JjiawRkW|MiIxYr[w`YKVnOk;fgwGvUrjSKzITS7hj7[U~>lm_PSeAZ].`dF_vAQr;\q?Ti<7ϽkRuHLy.\0I:(]_x9xRZrF}4r׬x|[WءneJx`RbjdxB׀bx>i@5TӜ|hhK]Jp|0CWTuOoOg1[=ctTLGvoV5aAjI+j8bW_Dv0Cj"ak:C-V@*+Se&@k+*BWZwQg]8(#=("6-BlT8V];\:^H[0`@v~MC{Myiɓx}x@;["Yr1Ti(9>EvpcIdh_zwknqwkp?j`*L\8[NZ9VgBmApNs>EX*/;"NM;pzytevn{rubm]bdt1WfHuas[\uDOxMeJf{@oQnnY|:WW0YT9\d?Zk/ES,?K&8C!.D1EU=_b9Vd:i{]rYayAJk7Xj@Zfx~Pb^1>_e]@;@OA(jC#Xg2Zb?Oc;FS?OC5PT2WE.FH.W@3\h/ztN>vZe_^|Ka|WheEktC~tBsIj~W\pOoc=Tf5?^;JN,`R#cm-s^9iX6vP7{k0xMW^wylueQ_QtNqm0;lZjyP^:zcPW\uZkރS6Uu274,2"*(7&/|#jkFkrvrnk]rSCWi̋~P|CzyhKdu@yASUcJy6kQkWr:[u1@_dbxHgctX>huj;Gί\dCt8ʮVoۢL^bnz8}rZ`JSZ?ML')mpTwW[uueZR~q=KKj7qC]vg^@OL^}6@gPBK.GJ"\+H[;X%GT*1UpzPOTS3BZ=FM3@])h[*mQ=WNx@urlbnVSz9`+L/fm)jI@?rT%Xtuo^oLms[Lossmsgio9҉@^Zf Pw͌_Uf9µ‰nq}[?9\.W.Csz[EcF\Map^R~SnJU5`d0dجㆇȊHoL.U ;79$Y#=1 KiW|#vUZUOqvW3nD)`Qk>^6Dphu[}VTe6vFPgUuX?}T@d^{KOp\\dKG}:Bcbh쟩goy[v~E{ړ{`iԀsJ_ai~MkSFo}r\6?=j/cnKsne=FL.hJ@m߉ssbs㏰jB|E`$PY+W^1_:fgA[p6_|O^]g9RW\0~qJyQ\ȡ{݆{bӌ`ϋ|ݻ։çsΡռzߞֲuVSlϚ΅TvfmnDt^vtamWRa6_gAJu|Sbp6^?gxAzowT`k7LX7axCr[XE4SB.cY2ouH|QsvUi{SUuYUkW\UK[Q2fZ0Tg8WcDL]AQJ9JP1SO2]F1aU7_8E~btZP|caVQa_8[tU7_=*L,WyifPpPglv^Mn:h^QE2ta(_IS|Dns;UpAY]Yi{75gosPC~cYpJLCbgY3s?vXqKZSj6hU[x]Bo;ٗuq`AyFޯdSQMXd^-cGYUvYGk|G}7:P,YMPq4Ii0Y{(di:HwnvyYVMT/Ma0^|.=[aXy逗kd_?xn<Ùf͌^~iw}@8YJJ~}Ȱ٩qv=='M8>+gS6CMl!@fTv!O.‘ޗ䃚y\X]AºY[ؤRVKfysqNK+{8lzFzWaPQfnRsmպVظe]d?HuwoHvV}P{YØqYY,|`Ic~Q{TYmLƋ^E@^%FH/{kxa|lOPK)?C$[aGuePs4ifGoKIZ+IT)`_.pD_wDK]viqM]Ain|zNjl:mBO|Zmu\\sϑt{V~yr㶱uYYƞlqVn@y`~䒮yeףּKa=l{gW@n'Kj@nቋicJpONr<]rxPafQƋ~`l^tx|wwt{c]9Qj>cFruN]rP\~US:Z{LpLU}DiEjw]tpz1:/#>7([^5Ou.@S5JV3KN8_yUc`\Ck`?bg9moR}[zmgr6^oG^oFlXzNqVZCPjEr^ixOb6U~>`SvozkӍ{>cxWlSdBQt@aq9RM'\T2\c2XR-DD(7K*AT3FO8]mPaKTn6Mf=Z]:khUA\`DaM]9T^BZhMViDmizFzQ~3p`AoC~vZkHeDk>cQVj}ćGmbdiQVrpuRZ.|OZp[~p;Rpeha~?jj;g8faJ[izOPPh4xUgbbdCjEizVUgCvAsjo\NopCyqDlsIfEzn7acPIX4g~7rHKung)c0fe\|1["L9Pi+Ft6Uk'Tl6zu87PS:%ph/f0b=i﬈dFde@}e\b_xDNO}@Erj`'E{kgb9lV.NėUwҔ|9½VnrwtiE悥{nʆ`n\}]x6sW;lTG[;<A@52B0`4^ru|a{/.4E7UC$=L MlTh"pr'Sz{P3@#macH}˪Ɉ|xK|L}yrTh,fY!ǛZŒɠZsܵѢdhzNDq[bؒ`jʲ˗WgHzK8|wW_g6Ts=|Yf3pAuXzQEL,}_oiEXN3mG1cKZynVM>dG1u>aI7uXuWxuP؝|qsvS[ٕjUbz=^~XdsU\;e[=noGpiAodNit\|vu2ESO|TiYqOk{GQ^+KX(@U4QuBWBkXyĊˤrH^jryZyLbzAXm7[n;Xb5bT1Va/cc1`V+BN?QaVo~Q`oFhrKsf:QYFvvULrNeN_RcS_>^\HhJe[9tY;G&E\Lfua|krasCS`BUuHOl@NV'GC,K[-MX1[^0Jn:QlKT^FS]:X4:]}bemZuamb?pIuNhmPkNDsW4{`8yG_zlTtNpxKjKhIPfOGS:>M0@<(Q5fB.hF*c\4][6fc5uL4sl;}YHc?lKolJGZRWO6df)blEpn>wJy}Mtd`Ys‡犑7de'.<*((7}#G"]¥ȟ˸{hGrFYdN_Q8_`F]aMdF}`nQNx׊snHeqH]RiwQJ-|dv5w{8tL^kzjkPpvHht;`s7cxaCA]auBTRir0u=]|tFJ`yv6cv{xj<{qcInKao1dppNlPnMxb0=$IE)+<,1g-6GUn'R.ij'ЖtSP(? BK Uu]|=B*wcqqSI~Ko9}֘վNOU<]S0diGbt-hȤŸɀn`hAV[mAbpV}xJNrԜߣZemUσ`VjO\Fz|D|e~H_x|c{_ɄpOV𘖷y[YfRxFYrU||{~L}Pxc¦υؐzdwYŊ꣔PfEhiw@sLqs?dc~N_NkaqxHz[U}ËbdpF}ڠ䰾ꞞlSnP÷XuDhtBش𣍰qڀ̎x[fIsx^O弜tk`Ŗө~QnlqƕDŽБ֏ҏNwLg_(2A/c~W[~}\f}+jKjQlerlrHPgHspFulR_pϞ_j}/IHKMbvHdc0FP)NM5Q\1D[<]~GbUgcy_QtAF1J`cBnEbvS}n>vGxZyZĢgyx\Pv,F>!04!!#"-"g"KVБzԜ@f@J:<3$>>$?W)T])R+Brc\m>d@[@4a)T_t^flBYkA`s;pv2cc9Y}n`kuVaY\tKs9L}afTDpDmaYfwhsxNuSzS|QUmLqHGL0b'ppQ|ngD:[L@*Vg/XV4Ti3p-|~hw|IZLpc9TMP[8]l0>b=/J#NNR{&Zs7b_t?`CgNWR/Ue%]g1LVOZ:`1@\vKgZ]YV_td}O|4aK8*G:f(If"H^Ct)JE{]g_hTO/G"^Q&yP@mI5# ,#j6j{QguHYW4c˿լЋa_;C%AVCX&OY-ct-ju'OyhC}LiFf6s2f`]XGhy8ÔilZIx.xd͈rpW̄wa{rU|LiHGfAdΪُʓxOq][RtJܜXPYO9ed;oe@oxAtMXku;sdeO@ƧjǕ^7loI^Z:̤njxjvC]hTqT洎x`L}6WlEtpvfftΓܰΧ]fb姼Ȕkr}d}JYqcrKF_7iuDya_\NcQ[ɡﺒxďφËŐ|xpԄS:QYbȚݮλ~[pDmaYNp]ẏ^wXMo9N_4UVsXclt[Vg+Vw0Wt/i|VRmXoXQ[f|ij~IUe3Z]:bCUYI`{Oa{E[n9bh>_uAauFepGlzHcvKP`ELuwWjLkPcMdZ`L]iN|dBpfOfo_p[{Og|8Zb9lv,D_%Fp2Q^7d\@unQ}\qy^kV]}Lb~blmvnq^iUrbUvUr^sIbW<^aNqoL 2E&6M:l8LKHYLOH,kW%cyEKpaKdP[Q?ug2i~?huJVrIhXLeyCnLNnYdfS^\7a^;uj=c;zhBlIuKXsͅzyE|Xb_eQEWW=qY9Up9sGAj[.vVCoX=jUHw`9u;F}ELyWmsUkG=sI)=X!(7A!!30&"2#>1];K {NrobP]~PE`\N}zuYT=h:JJ0[+GIfQ3~>[Lr?z?@|fwftyfwTUbg|zHrgJmqmPUNw_3Tf[$nAbaoR^qZpZft:pp?sIequdYj]}KwIܮ[uVrV9mh@xgC{QTW}i_+Ue^V:s5]eOkU9VNeh?\p?D?NV$1Y/KQc0@}5{U%I_Mdbb+tKAZ<ǖcGbKUP}`\EpbCi9UALq|\ٖⅪ裶|^NohtOiPjqxeJZMi4IpLzԑ\z`ozǖΙ啯앝߉mOw2Ep3T8H~?hx^nGS9ND\^q}ҨɥҼn\b^ebqQUT\oߑ~kÆʂcMks\lliuw;\k8Ne4\i4\`9dcAle-caF~cP\zOkCXMjVeKPbLgBvrQkjY`|PZyMpLa=G[6bd:_w>Xi0McC_XhdzVoUaZoPqVCcq@T~bwtulxA^mYd1Si8Y`Csq?v{IhvOvQ[f~rO{eEIT}zJxvIKoA[{`swzvgviGoA{AxG_?i;iI{eGqgMe@Fa`~ZbetR{7O"0B!D=,%1*+*]/P` 7I/c#TLy|2R>_w$dhA{Hm?lvYmJLbG@Q½鲤廁r–fpڀȸƃèɒ{gVݟ[aw|C>g%E[$[y,~MQDǧ뭍|ٜm{Zw[hLOA^`,{d?hߊyǯvU܏ɘ׿Mk2e7wtltP_Enj:zPzE{Iرnpd9]8{SPa^4j\EkFQv5j~DsM}lFe?kgEay}CVEX\~BrthoaE~IuC}}T~ࢹ䝴펟҆xiHpvFt{W\KmRxݶ΋yZ`e_zXz[o[uNnR;P=rejMvoBk+VYnPMs-Cm;PKjɃuwӤӠmxYm\ʅـ|w~lFLwCa{Tyig{c`A]q9Vb@dwA]\BkVeSv^ecyhyi}XwX}t\{EokX\pO^vMrd}5\l6Qz?b8D45C76@3;8&=='XK,h\+mxI^rBckBwd>InZnyoowVZgqfa^d"$%!*)40U:RS$5R;:H5AK{\*Gq}`w}s]I>eŕUgGivv:Kgl`oaqtoWfME{gxdFoC[5_\%^`>RaAgj8l}TyHfyaJXCj6d_.pBlcwדgiWҔYcrhw]l&=a:m=0l;nUQ;Mh%"4oqTV}/ce02A9\3ujōсzsXnhɃԜ쮾ߥwȎώoĞ|r_vmsn}z܈͈ႃn{𓃶urMVlDitvHRbi]wuWNLY\EgUGV\4_j=kX9qR6}bAlCP^f{gjfƎ`WogYĢ\³esqd†MnDW_>mz^@s^|cpd0t!.K * 7*`N|A?k5IX1;I1ZD(V^6zi>ٜ`jހ{UdayWQey\ISxv.}Slf_pNnzFA3=_)jZ#mJQCwp0pQ~Yafap8[8e/Ov|0b\~\=]y}؉s~Ct]TVEʰ^[=sUrxNJh`nIqPQǮͩ`j̀GZ.LG!bR%tK[Lَwl`pImKg3xRx隘Ņꆹޗ㢤ʈ݊_LzrʁbJ|OfwG}eHd=L\k?Oi:My.Pu(x?oB]rNF5'44We@vnLTf,`xWžjtmpAvGg8ghH_jHe?W~a~ν}nJS֞]絃w|3m8j6vE`tEQ𪒗ylo>De8Vr-]y3gu4΁OI&h<>ûTPYy{ȷ荰ӑұgdzd\i=T8zuԟ}ҪO}1Ip,VhBbsHTɐzȅpw]am9YVCVQ|'%:!0BH,Be$C^ E[0s^ߤގ͛m|ʏЛʘ݇WJlgzH:TG}S_e;Pg5Bb3Pf[uUNjE}XuGgcq{硤އd@Fk6Eg2SyDopn`OgEtPntαѓАx͌oč||vtYqXu|ʆٟ݋bvHc:KsczZo}vozsgpO[}DfqK\QgarU\or|uabYZw]frhTuZ?Zf,Ns(Js/P{?H}47_F_~TkKcUhNfy@XpA`oOWzRlp@jyPsi|fnSb}@2' R,_Snu4|@ٍVXoo\}]dgSqiBxjIpoOO^>a__4l]$i>v}Z\lm®MUj4tyFOlYngf@v^A[mNeiBBkqoi^k{FoHL_YO3[]lhԇJyrZXQw3i~ahHvIiu>|rFTބɁfwlij}M|XhT#pFGr^ot:GĎwxoތxkz:ĈJǏzlxmKPa}`x|}FSv3\eCOjloڢpFX`OԄtLqGyl̊w̩Xr5fY퉟`xvoEGKoMvkCgM{nZYyM7IJ&VG#OH#_FomCBp,Ik3AT#`L6oaC}XŁ?hJdxH?.de3gDď{MxrprjΝde?f~Ne4am-R3eրhf֘ן״xwwXuzG`8la@JxNyR|WrAFc3ly:dAMf.pg>aCU[F-fK@eI^%ok>Z|࿭~ƭшLDCY#FQ(lLx^{yYfעyma?Ss>PeAYVy|ԙYZ{JU]E(*:-I%0U&OtITxD7U#)E*cWcm]TuxOqU}wpqт}\WqIp[sSSm8Sq3^Hyd{G`w]L^:Tp-Fb,FcDuzˆ֗փpftDR=On?KbB}je㝰薳}nra`Yweuqvov}`xeޓҀ{]\{`v`}[oqq\fuI_yWvx{rx[bjF\|Yd`qav{saQtQ}IjUs?Zs3Hc(DmCcZkMkQ]H`YoacWiFa{BQsIWtDZp=P_A]iQbtRX|U^?E0>Y:!XV%]^9jsV<|;|\eqiu\s|CN_zmqmq^}cuoy~g}rF_LnpMb^~OpakVNMT7:@;)2/5%**F* BrE-tn-.H6.59"6F*-C+P7 aO%TaMfTQh[G7WE!E5&BX,li9~n2_JNs{UQZWYn=h\UuS};]_rs|O~I]FZt>oa6V}aUG\h=Bn@;`*O='?G&?F'V]3kv4WtU>k:yY&V}Sao8Ee9d+T}nihza}tn`NmmmqtLcvh>iBK`gI|fVjRu'SrM^.ixa\<`tӾ[vsiDerY0^v@W􅰹ϠkБ|zVjBRu^pSkJGl:Z5ElkqydzfoFafՅ@ꅦnMKˎ\RA~79T%]$}؞wVigR_'wH>صQʹwrb?eU][\0ߠGJEC)QE$>N"?.}qQ3=al0X^,|m[H'WuÂkNVr0_g0t'|NyY]U|㈺Mo8zAoE~Bl=~9ފgCbZbLqlE^ãgwѨyYt>ov7prdVyyxuhbXx^fu{`\vGSzsđǁu~s`~MapBUhHiw9Ja*=[3AnQaZ`j`LJp@Oyj{nxXxVeQiNDtNb;CX:IdBOlFMmL[`.JK,P@2aX,cr6bd7J^BQR=MT4\Q6^[,pY6pn8jOfOIoLgORul3xTybwYucw|Nue@jnO_iVuiE|>[uSmNRxCmQ>8G/8^6)GP3=;&0K=4.!14)= 0155*.'"+*<"[= Z\57\<7G2=D#1J(F>#<9!C7,LE%)[34/'o)Sr0J1ChjuLh6nWi~[qc9rKQysikuw[]Phx>B|K]M'ku5nrQruSl|\SuMGt1@K-[[-Va9Mh7u-TuSTl;^e.Tf:Zm7Dq^?Mq=Il8jt2o=hOW=QuO|s]cnkUL?<ʵoγykiuLnxhOq8dFuThm;jBXsMrKUX!^::/1*h+\p1MkGR%Az^fwUEBax9rhjnL\w_ighGyI{[Y{X^|Kkq:IXcfDfIlvxn`kX^gA[g7UnAay4^kE?r2NT'Vg2P|GMi>]y?_m:mPeik_FIpmopLUHA^|uc_HOj:cl2FobWFJu7,:Bx\>ztXfpUNCTQ7iWAFj@QR#Rp`PPnbOa0Et?nd+~MbOVnẄCؽT1T.[_'It@rw6ijKjmchOM_Ta2p}3cē҂thp=Zt;f=:^7I_%8U ;IXy/khBOgJg4tN[9Rlg4V0~Z{wePmL]{>}{6}OI|YyRd]MKO)Bx5r|>vy4a;jjJrt]nS}QpBJATY%pPLOQmEsGhoc֕L>;`#]T+x;|f9M{zpC|ؗ뎌ć^x<гdAꡈ~LOEYK4gbN-pevrJwY`x8Sj U{)Jb!H{乪םހق򉆿qbYB~fDqxݒtbCiyAXyvhwl|S`TyMĐƾԟV=Ko2V{;Vq;qTX:HC,eaP2Hj#RT&o.i.SSėcg?Hf+jnEfn:sfyerْ凌Yax>GRB}dsavwlxK5wjcaP>EHoZmUT?\}:Gp.JgWg\f>T~-Pm=j9apAkTdV~~]z~aYpB\LlMUYu\VlIjckZ^bdqdGS{6@]0GiA`P^zALe@fdukx^]Unlv[`xTn^s^~Re}O|UtTRa@lxjyȑxdxLp^tjdtj~tjicYuʐ}wy`onSm;NW";V>IdPPp[pxg~[Yry`_aemynqmqY_mJPmVcohj\]tHU]:Q_8=Y6;E/NAAY$HM0[Y*^b2\f6Rk@abANoBZaLgT6cW?AZDPDIWM.T]2S`KgW$Br/.O1+""51;@UF)gS,m|=BoE=IJVF+FnB9TA==&;G$F8!\4MxakqhQhR[hF\[ohCl7q_PYc`=cT>UTltV?GmO;Q]vGYAZ|>t1^|TM{Sop1rI_hu59tܔ~KHg0Wh1cx7FqucoDW\HoEǍvqbEVŸ~`c08[p䈲wyj_:vp}Cci@]-DN/\Lx'H֨v܂vpsC񗟊kLFWh]Ckr7{h9k`ЀShgFRLj'Hk(qiCyhkhp}W?_g6`hsjˎrk];jvHioa{Mҍs`_HjLs…hyjEgCeySQDCa4;S5KC%MQJX3WB7eM,gc8ReHJTIdL4W1{E|_doWEEN89pD6x{KbVIn)\<1$ZD7\?iL5^/ldcfS/ld:P;,tG320N=Na1VdK,KY)O^.P?]Qu?tX_\>w9CM'eM%y~H{jKj]YiAY=YPw6rXXpGdbC;sUefspo;nm-8WVA!TY5O,f}\GLh.pQJRd](g;{h̚OYE^^1eF{^;ߠcrƣ]ZQi[Jnt7TbsQM:q5d~DGjap@V*08R Vm/rxeaK;t6,\kB~r^l^{O|wbg찕oqa5ПTbj;Ra7=H"BJ)yu:12BAc/AM\jlFzn@~qLѣfo>{bˋybeshCXpZ}xXAKtУɈС|⒚ԉ~dnZ~ڥN]j2^\7}Eq4HD*v{Z^}:>D)lZ;_V4RT-jQF`M›TTo7Vt0Cf'LW0kxkdpC|eC}C{KnBo\Q8Ox-@I3j㥵蓳~_ker|{~Ҁq{niszMoLbI:K#9H0[`qduhїĀ~`]uC|IcLiwNknzUL`XxcnYnlن]^q4?d28d5IeO~_}jTj@nWuXm6Sv6Y@`>aEbSbbc^vhzF[wUb[g^g[xszv}}YSz;K[p^wiesRwCWk2Gk,E`?GkLWtGVpLib5+19FVFZnQVv~w{^p[e{TWyQ^_ibcWa}_glR[pHMk@Sa<^_4Qi2>NA:N3\Xk|<>~DPOkj6~ONQs;i^\IXvJ@p}]mF%D7.[&\4hVbSc_5sFRJi\8S=M`6j5fNIl:?rx¡f>`}Vw|s3Zdys5NZq;EIzi:l^namP|~q\?|ʗUCeLS΃剳t݇“{cxtQfTZnc|]Nf[;ja4ujzVbkzw~{LvHdzVߓxsCrz=ZaxxJfXum@Sg5vvP\V5Ra7E^1bxMcgLBa/jUBW#fW3[Kԇlz]UM5Z{nYgɼ͈x`fdpܜыE4iUCeS@ia8cf:bA{kkRd_jϜЦ~qt\`}?mi~`qe^bzATXtkl][Mp̀v}tVtQz]uF\KgxΦ}ќynqٌ楶~Yl[tzƄ}yחqqgyLb8^:JqAgcmf~FZ~Pf_NOiTM4uU19WMk*cSD;/^Mm-Bmğvoef{qxaea_)F{"#1  9_9fb7bgPQuJtgAfr5OTUfD^=>Qy6r{KB2ud%s6JffâKg^TZKig5uF{AIS~RP[A`k}}vmUghhQoudjcsGz?[zFCn:NZ+qa2reQDHx~ieBxDnnPpk@E(fP:vmLnlVfSao΍^n]VuFi[AvNhKMI~Yȅi߀m|y왜lYK+bS0ZO6-.LK/Y]g{;`^<|T\nR~ABX5\nXqktbsQb:LmjfeiZ_9OjGhmob|HAoActƒ|bvIbsHQeNs]bsRjhpbgADc+ISPH3Me2hf?n?Y}wɣč˭nz~ϨP|+Al$&6&'  7 H7EO2RJ3MY5PbA]63s`u]RdCh_XJa`+gXag~\rzAmPQbt{n]P__hA-p`wDX7JRbIK3Qq,[o4h9؄KZSI>@ؓԺïuNgQeW*T{JrR8FWqJ5KzƼq`;bZ9ms@Rt>jh'Lt9tV'LY\j6Ty5l=:hHcP#]x@f>j~[UwC]{vcPezdkbeYc~:euFOyhMuqJ{XpW_FPzNYk)^W/Z?2|R`q:rAtP~H}_oN}P7ۂRύY?IYMBNy>a](XӒMqNq9Fp56\39Q=B]KC\ShtwnwMNl#1A!-A*?I3Nb6[f7WS0NJ0Of4Cf7FZ9KR9>_yb}}}\gDaGfA]TpGo_AYoz]gy>orc`,9˳]t؟{b`ʈibd\uFKO@pt3acT|G`vu~`H]{OfrC|v>|G9td5uVVf=_wDLH;Z9V$Z[[xBjd9]W;h6AW6aT `>a`q;wN`{vKjO8_0KA'fu/kk2eb2vhdAQfU](_M~Bkq?EM|7Ot1n9s>}d=vԤezsW[hERO3:E%}jCݬϳe^]kcWՉꛯӁ֕ײ]ވLv{Lj^3=}LfjGɠpluQ2yFiVfV-~`JsknKy7Ij+\d7c`i~cn}BhӐpFpg9bKl\[y@Jb)Sh,8t,iFa*L^@~WWGf`oĂq{zwX\tLԚ}Vlrm[XXPr1:L9e=K[@wjY~auNhMLs=VsiqzqHHy9rr[_\d~RUFhi|{KvkrnICh8QmRW\8Di2RdGs_jRcI:`-/G.3N/B\vkqaVi_je|B[>YuDa|Iq\qMZ]Abkfv~pyrra`XePMr:C]7UnCSn<:n65]<9c5AeRkt̘y9>\)?N&5=5NiDRe8PW2JMDnG^HSqGXlLYuSUbD?J0=QUo|oz\zatTd`BbhA]b?[_2fP9Z>{HW|des[gnMfsH{qGMP`p\ؖdkśǑӖĚpoxbס\!9D '$+)$*&.$*0-+..2<E;SO&YU2KX>JaE~W4@}]gqea[aqCeaFvJ4|N3{;RLS]ϓvŎbi:_G_Z$z1Oyv~gZiuZqIMrJo^4}NnlogsVnRzs\UxvoUNUH=L\^oLQ[=`\-aN7gAQI9UQYj=_h._oF:w\sYc|܄{tseg~RBr1AV'UQ,Yf6Ui3La8lU-i=undFQɚr}[]j]HbkAKmBX:mW9v]Bfg@$mlbrM7VNKs>swHIJxX69ɂZ|v|lox8sL[jcs[XfXץ乽ޯƟhrE=@e%9\#>Cj\#?U/r?'OZ_t?hQ+v\rgIZlruRBB*i]l(K}bBkszN،c`vJlj-nKOo$懲~chUo[k}qQ`DT%pJ-cdʠ]Xgz?{FѼ~S{YqiR`tҚqLZKY>u`BP)dk3eb0ycebV,yTvSKg1hb0qNjCQ[`se:Sj1^qc:g]L(%&*X_?SD)V:DIeIyU5}U~Uuei_WAZv;UXg2Xx2[d9[a(PK0~[z}gHm߄p爍T_OkiN9'k32_e`JyCcP9{│ʀyQgedBjD`9Y~;|jT7suIZj*X3xV\>Jc2eL\xHbHd@czQ{w{zjdul~go_b8FX5jz|ORbKleoa^Q\J\mF_4HX0KP2kbIuWWv[ky}{P~[s˒ޡՓ_hgrǃnڂ|tsngciIcgeoLpNaSshq{acLZZpargnWhQKo=4UFcyjiqi䉞NR1I{1H{8NE@S0L\?\f~y{wzuv_fJQt5;\CV]oXiegOVIaYk̖ooG]Y(C]@aSoC\jDmvPqXt]nmsyN]o39T11YPW)Nb.@6#o>&uh_t@b=rG\pW|MjqW~hu5nj7NNbja1tLM~tz~Zb2Q.nKlwx{׮xn׊ҡlsfe֬kqNj75so[`PVDW>!h6$zMVrcA;iOI10g9N?%("& $ 6$a72T(G9/7V$F/-**?!4!.&UCm Dgi|{foCZsCpJMK3YU*rR4nY2y{EmCOnͽo]ob;ftA42`C1623RM.I\+eR'cPHoK^qiseqWsoAsM__yqmdDjSuE4_dD\rCS_j^>xxFu}MwluJ6I&@E_N]'AIw6(]5`;Xdi\j6Asht>xtYMiGQPuP|=Xl9U}Dr1atf>Y@h>PۃZFY<]$aa"kؠۣ|ܚ뤟ȁxaotyh||qtTQ}*NhW]`AOv}p\Xmc\tf1d|c|iEto=d~uUoLpC$pxizkد{vgzpk`{Ė:T0l}ȠiPnӈŬ뇐gPvzTFԐWw9`u1]g/r`}Ep{FXeG֬wЕ[ȹjW_ĘXjWXjMWzr}sM3fW4]ՊҫYf7lxD\U{9Gm8RnGexYj\TN\QUDMs:Yr=Rt9IzPvi~bw}닕pIWi8PxBD]Aymw}jxy{oP[u@bSt[mYzio}ևz~[ew6?S"3M0OpDRg\hnvNPg'1J(8U>Nj_pijw67@)<[KQěϡo~Wm_dmWjrHetTnbne}ukubx]ZrQoM`Tg}KXlDUz_OVKzZ9lf7q[GacPVcBPV>Hb1[T8S`5T[:_X6gZ9V=zaAlXHs\F}_BpNSɟuyoz|ȏֱЯuc]R[Ji\LS8:`F71`U1shAkWJc5u`)E>*$ #&("F%V:4J.XI4Gg.@l:JS,3]+%@/<%kHp%bwlebtqOY[[hPwhAZ?Ua@h`7yz@Px○ƇtTjzMSIY5S.Qd^0qb46:Fd<)ul8oa6LiBhK,|3ee7E^^/-gg6l9sg^oK[MT\FMU@wc,X}ZrNMWHgEBC,T_*8YDSBi[3of5aOt9KknFv=?].MY$e0ZCs5bHi^Wg36FXXS&|\1OKIu>W\3j;VhHgs5tILkMn?9oJwÉnt\SS_IN5IuosP_c\iD_~KwGÝ]nwq~:[o:ct6~JL|J{4_xPxyATThR*}vC_lmzLre{xvsqUP<ǀxovVTZ,wKMuZtB`Tkk=]{>PY)ixmmjZŧWpHV]hZi]?Vi4LGPVk7dp}JgoXpPƤyQʶgjdcH˯vɠ|dd9fb:pmmxBgwDmAfɸh|@M!VH*y`@vPktF;D!XZ*MN*VO)iT3vq=Id򗔹v\Rgw砢Њ hzRk=K[:shHhywxzPtX|eU`8OK#V_3V[?sPfsxHe?l8Ki,^Ggz^r;9?:)DP+:L$Q[AWzB;],M^AKsL_JWzH`WYfRN^q=Vh-ET/@i@[E\^SHrXxWVo@oUxIWw;nyzcoRmh_Xr{ڃoZeVrPjKZGUKIMh_yʆ…r~Va[vqtvflâmLc)5-^4\#yYskVdfeMeu<_jNgIrEz|XZln~G{`?ZqJP/gb-=G^B2c.jT1jCdUxeorWNY1g+UD^^-R{4WZB^2frnzGNuSEp7OJ'^i2xDkkUe}U`jXMtDRZ:ZlC]gC[{G`z;~pDz_thSK>2Nf%dj(tPVt{pAyC\ҋޓljLׁThEd)?V!Rf!k`%mDr>xZjq8wıu춈cU|QFR/E\/m~ǀ͆QTVe,aJpWe\}Ei]SZ.vNbDzs}kgkL܎_wgg͛v`7Y"YBmSitL\H4ǟdC0ouRlMdIgɳ{QN^duTUUǒbiёܗl`Uk]Qb_s⨵x~ԌWe[|ktjQG_2Yr7SwA}\P5?m/w_l~]v[kF\|0[~;jSrrpEx{EKq;E\7Vcbx^XPnbeXjlxltitgv`d\bFOeEa|\n_|vsajxIis[rhszMh{;PdIhzZf[pSmeXf~JOjEP^29Q$5R9CbH_aSbm0=R3Fj[h~nk``}EeqFWnBSyKNrI_RZ}LlpR}NmWHeKfL^e2qA{\rkZjim[e{LlpYtgLokHlcKshByIZɗclisRYY.Rusã}s}nnt|͵lӍְhApKYFVa.ng6pg6S`>vc>v=jZgL5zECH4?e$2d/J7,`;l^+zLS[fiVn~;dyPhvXwITվݐ̫]KكͨcWybcJztATVw:fjqcKoGe8ɧ]~{:y\[K%kv.Ry:WZ/r^6~QbHc_t1SabQjZyfY}tqtSqSdYVuIH:RS"THxD/UyGDL+ST'`y4=̉ixE:c7[]-uq@nKbR[AaK}L`؅uq֎`GUKyďS}׊›{TbTenK|rtFjVmNU٤ocak9jk1|`Shl=BvD\uUޟu}MǒppQJbmZT_KVrot@mF5Rwcλăjn@z~S}l@ÛlSnRzy^AgKwOdph{ai<‰ue}caWmryHjsjxIlвvhؗˍkiTWr\SWqgwpրcwozjsiJ{c?aA|]unpI\QUf}l|nsi|V~Ux\nfPkB`evnsuĚС韪挥rjWxi}tw}xsIO_]l{x}oq]vGSwGLmScxQkFd;N_/@d=M{KINPSUIM~LaYnv2GM'?V2@a[\|yw~gs]^sWiX^P^uEUoBpkOU^gBddYTzt2xZmfWh^vU`|No~O|P~GrQzTtcd{Tx[kwzRUP1nBt_gnsԗ˰Ū|pxגߵ}[tXmWVbDbR7dpkPkvhfWKfSI/Vm5c:͔md?bI7=)1399OF Vo:oq7WP{tCTuca}>XrSCyIKl4]j.jqA]ݑj^iC~:h`[ʀ\oiJU@n&f}{QfNw4RslSG~~IU|y\^筄AKYH]$ci1~xIZ]z-%G*-:QV#O{؃GnTKlq_Sh_BNy=CL1@@yx?Xz[Qs9=[(OZ$iKj|D]coOZNtQ}Hcfi:^u;Ka/fCvZkĈԌonoBgpk\g]ӴGMг۠h|œXR^oU]I{Eg~;T]UZAH\,cj9Pa4fLVseˠscvMaWw|HtD{~HyEݡlw[zYpwNip~fjKpSi}CyNzJ|[YI1|`meU{cmv^oॗٕ}ŨʅVaKdɞlp]RklY:ySǼޓkR|ckᲛ{wO`OjGhLiĕj~JuMpvRZc0ez5NN)dQ|bc{n߃x♧łޒ`byhpfunNW~8tXhOzZN_M\GirC{yIyz?YzVu~zy^|l__nJmfOxVfIn_mVpSlVfNn|[rQhqX[\=UyFV|Pgtpܒ懈ivnrf{a~eҤmzNiKxF\O^uHmutfwlTn\u]ok|[dp@QwBN[otĂxzy՝ϣ~z²vycwĄdecqqzqcuYeyH_sK`|?XeOYtmb+:Y8R}`lk{i}f{Tau.24'=I;N|axquik_pcvf|dnXd|O_{LX}Gnd<]}RAYI]NbH0tn6i[\q_ylmsLnJnW`ZewX}d@xm]̂YlgyˑӨƶ²թﺯɤ|tyǜVXgkXU;Di:L\6{i7UyC\YC]f-^SA|[3|Ea^wfVgT\LXV:fL.Yn3g=صQݸγ׎hÅxvѱYoOYU`Tu|cNLOkIR.aHpmKnyLgzOw|;sYSUpadEwrCbYtl2hXSFEx@gz:mqhp^VSR]r?jzeyxRb@`\<{pEnOKW}VEz:WT1ei4M[BFd0Nd,dq4oDtNhanzfzpuRgcJyV6T3/4 LI^d0mh:XVRyI]c?MKJk5Cd7xn.StYI\e8SndqBdaqL~IliYJc^d~BPpv|9}NUqõʡؼ‘}ԩkR[DCq7xr%Fhh/3x@(D0CCS sv5V{Lbf]|D^z~͜T[Hes;qJO_:[q4~`1S;Hq/c~Aq8mHhv_QnNulNT7v>r]fiř\ꭡؤgȩreA}VpLgN-ɋWk/F`dVLCK#TVҍyDa5MHd,^P%IW6g;-wsaIW'iQ5]oEׇ̫׮mOyI{HGVckEmzTxgYŚgH`sHfޓeyKqЋToGnƄϑxq~h^QawzUipQw^Փ\}gR5Br&9o"dY8‘jˀyNfezSjm@]䄵쐣w̃nhV]ªubjg`tIT_/FS'frP}ℱzuunfɞtmqd‚U[;ZKiPiMtupnNv\s_qKbGj]{Q]}n堥VKsDkM}tlԷtxUsTdQcQxszzҒ}YVNՒۡ젧rv[RqBvRd]ppF]EjVmTo^uDPzceimK~~Ʒmy]M_`}͈Ǎwuc{Ja|MYp`nPh~htx}Ď~xk_qvpm|KWw0FaAUtRSп11F5D_Nbv@BT>Mga}̂H6O/9WG]dQ[kUdxZh_jjjuwkRUzQ^P_{^Hf-J_FH[;8Y3MB%dZ'`lETrVeqSwChP`cEsX:YN^F2gMyT+j6iRzQ_n{ccVl?sI`|oouaey’޷Ţȴt碃zErnJE@gC,}y2\XDbQB?/BJ%KJ1}N,wi5vTzRdOgYUkNOQ@r]7Fcɽۦ܍iҧzoOӏPsUdPx~nCbrOWlI?fGFC'iK'lUW]HYa>dbG{lDSYzfjkjyiONs_`2P}]f^axCs{5Ahwk[iqRsu>O[ydEg:@a7Yi,px/@×Zxia^rZFjL]eATF;Gl6Wh,Ya*tl0\jgmQHqMi4l}ʢsnUteDj=Dl7Gf4Wr0@X:`,WttjuSi9c1Wꀠ׌䁮_gyҾvЯoOazsΎ6j+0;h3*A$u0mT=\G+Q"q:cD~r`t5EdpAqq:ss;T9%X;,qmRgFOL)gqCVyoM|ChbA|QaUaKwhn8|MoNҙtڈǓͨ|ٳƊc{^B]dCe-;VDOd86O.:V@WwnМu|MSl?O`AUpB?_BQeZo}Ō~wt^nNedD+=A$EI$HK-OY5L_7b[Cys=c`SslE`R,M?CC/SC!fR,|N)a>gMyZ~_o]sNxP͊^s}kycdo׮姓ըسЭͬwsu_paIk@Ld@QE8T'FP*Qd(^t?fuKZdkdQ髳w~uFE]A_]1S^BH^3aa@PLJF?1dX*iiBgvKySSp}RgS]a9\lCKbGId1Ca0`Y+}DC@Y98R&FBW\*]o5z};e]PeMc9U~a_|E[u3v7qd\}y?rWѩMGnw,WtHVn4b6a1m=fV`FV|>Xt2V1gh.w@l\qZiPX(>c0AZ*du*QZg|-{WxIhaAUizfj`aHfGkN>xIO`,V{/V;J~v]?YOgqddsN\@_d̋9>S.R|hȓYjLȈeqFSY^oTdQlmd_wfPVJU~ByySrVZUx]lJ]mPrzaekqbnYWm>QhJSzVjO\?NzLqoxwwcfLZe0?jxX8a3EF(;e-wA"HTw]Fb4Z^,Oo:I^1O^&V1Rv9U4eRv;WʜgZ߭[qAQ{;WY/`pHY=j48xeuaYL_0Wp4Jc\}|>DW^s-_DiJyQmIg=DcUSIdA?ngGGS/g*lp^6J6a#Xbb4oGfPxFvJP=UW'RJ̕Pیƅ֜{i^uT{H>e[\T%eN5qЊ|xQvxhF餮oWwΒۭԊsDnw=ZoQpPZ~tbxX|hTۑʛT|m]TnwHkG{_LUVS>ѐcmGnMwDvHث}T_}zRtUZm=ymGsNX3kFLX+lbCyNouG蒗ԁc|ZnGp𑗵\ov}v]mZTNUVv~JUqSd>_t2ciFi֟rXUlgZAzGkDS<*OiPWgl>c@\}EfoWVyi:yoFgmLzf||N{cYTRmuNnwMZg@qxQYtKnKsEcF`}Ym{ߙ݉om[[UsTuQkm[nk5L~67S0^rEM1+@%6X7[bst_l[}fxią˖MQw@baqIdMta[nOjYhhx]l\o^|]eQuzx^k_u^sivEMZ9@^Olco]lQMuCLtCTuP`QkIp}CWdOf~jxhlbjOOj;N|C9d=Bi/:U0>_Qt|{十lxXoI\mCTnQpixOEdHH~aYj}Qds0Kb@2K+/=*->%4;ABGS&lJpOtRZz禈xh΁dfzԗ‰zwszyΚ~[moi{JamafirNǍEeؔŋ׶kW|Ver1N>Tu6Zq3Zj2do;xGnQ`~NhJUEa?TFvgG}Bk{gQmV8qg;ZoCc/ӆW疭h|C{YIqto`_vQ]̲fVoN9t\2Wv>@rAPV,3R&h? k8qqEӒe}waqPaz@m;gazwq|oҘyvb匥deZnnh/XS'\eBTNGp5p.\Sdx>qLKZe|\MP}4UC^G[DX8kBrzaGaGdhu)I^5'U}oXB.Yx/p;_GWsIuK7FoaD>,se+N|΂s=oLaJn]MAWk,Rdw?߾tehW6u~Kgs{ͮaylzb]̸֒븺ƛxzwP|JaeNx`jmS純ܑylǎlky~JnN|W\bxYJ^Adc~rjayV}oFs[@fpC[>keRS9Q[~Pyӻ|sJSd3\iCoYoYdgbiLQSp|V2AR9RYSfʋqEak:ST8TYOgrWoYcasQ\nDO`?HR1GY:,E=59$:>NE\Q)8W52A5.H3 ?!/#3%/3"=%'I0!EL1jF3bq6lxTxQ]sPXjIl;ur@iErKOxеx}pt^kជ{ţ䧛ˡ֑Y}m]b`>{wM`az쩞xdf}j[o>wMO@3Vg(Ro1Mr/A_,OI,gb3RB\]BTw=s;KJMVB;[5:A(Y6 sZ2XWTrNovtr=Ql:M6sAY>}ꓣx}IoE`{}~fWxDxkΧ]|UrhO,?hFAoE\Y(h`,jb9TtinXqSrFyiWb\~oWq;XXiQ:VPl;jԍǙEhvoQ%҆HQdLVJ0}F$}`]\|R9HD]TO'Fk->V3"3!y*aX;eG_= [wHQDKr:QxCSu:MsDMd:eoBY}:V^2ZKI[9XV1S0snWuYW/[yQiu/Om{]Si+Mt4o;jVKؠɵŁjm埮z΋ۃl]fMGClnNLMњUj@oc>mhϥқwl{ZפĬ_sNX?zRmSaae0~G`drAjpD˲ֵðݟȓsxGkfs@{SngqygBxVim}Civ?혎]}SrvBmmEeGsK__?f@Fd/pO`ec9_sNZVWoV_`ieciws{OXe1EP#5?2=XUZmxm\_F_c;M`.>I-4OEQmL^o7=@.?O=Y}ET|J3;31?E?cW!Fv:>[E7M6/M**>2-A?-Z$42)>+!CC&Z>%SN&[^DOeBd^A`2BPs[ydTz_}as۬Ħі|isgz`܁nv~ƪٿ帴ڕ隒ѣ™zwMiVZBSҝyfoNYOe]WS|G=jGEN+I^&MU!Ra(Jv7]m*;UX[rHUHNa.N[3OQ4Bo3.R'S6kL!Y.|RaenggZs4p]1wd@{Wp\yawXxmTC[ej]?sa1VLkf=o/{Ǖyk~WgNk\)Ot5d8Qs[me9R^nruqHdTIj=|a0A㥚}QzHclxMKP}\bڂYeQTe9F=JRȁxN[uNi`ruJn`Y`pJpgyZ\OXW;NM5A(?U*NM"ph.YVc5\xMr)Jc&M9'Fm$xj RcU$0`#@6GXQLˍ BLAZ4-T.F^'U@CW-ZH,afCidEScGBP>5],{\'zrRl͠vhfY2GbLU`'D7*:<$`C^DE{4Zc,zJ^lC^ퟆ~\xwT~9HJ%kVpաтvW܂ىIq̌F|{^N94QwY5Ί}ru]^ࢧڤҏܯqWk^V{9^Dtkd3}Yޠi׷hHxSU_bl5}ghNvh>p֮δa}ٷፅaqHqYnyKoVגrŰfIl_akBoM|XtuIeeuH^uLqIuQXwWb~QY~/O{?qdA͏n]^y9OS.kHal}M~w\z˝plJuQ|oiFM}dpYbWrW]dєyU`{Ւܛ~ho\Бzdw⏯{{uaa|ZzZi|CySkrZ~Zv{_qDYv]Sr}Y~cVm;oPP~X}]^`o}x]c]eeS=`TgD_jFo}LGU6a}IMzG`QjʄepNcJ]w5^ugzyAk{QxoQ]FpnmuL\MiO[XvosexVoGMd1U}IhWb>MlVkQ_isfhslvm˙fd17A4=oQb[nhy{|[hSTr@Et3@c79^DAl38G1ESEThQfV1J(AA1hZ/|=PR>oU6Y84L'3?G-^QHq5;RA;4/:?&@A$O> [Z/Tj8_p8xi>@Sx{Uc\lecLtQuܮplXhSw\oƴөڝӺ~W^u`JpYSL]O\Qac3CNGY69c+6H JI[]'vy0Dm^tSXbRu:ON6ZZ0Hs6CZ-CLtt~|=JGOe7%>먋q_|`VcVf6}ѹnoVKXmLki4uPPa]q8KgIY)Ap=H0AI#ҢBQL\/VL$Ot,VK(ay;;,EDMA P[MIQW)XF^7`Lu[hL=jAQR,]`@}lTRpFO|uoZhWFr4N_%uIǁyR²fhQtJJj8md?mL|ЛÆ_xuMȒ{kzR@uTY{4;S2Q)!|duÏw֗UސrKxnhXW}E\c8|`Hnl>aRFZ[<ÞΞ֔ʆ҆]syuA4l.,%PQoz7ɉcNCQ^+PAAg{RqwǩY]e{Ww_]lRlSWRίidqIyNbnةǤxyXՐ|bjy}U`xHgk?ȃiTSwQ_wHormFwpN}nFLo~Un~dɃ̥̏yUBlO6w{YPm9`qPd{Q^mtxߠqѐژ޷zqToen{GnDc{?qqPuVxIrI|cL|Uhi~f\mn0`qFjOmAznnPRsWdևpW~:ejUe6|7^yQG^Db\-rH)^{gvhp2!9&_F_vuc]J7w>wXZeUN[0VY/MU0]3+Y8eDytHgrb]A{d@Ħü~ܕptPSM.nxrc}]gdYU*rVA]$toIcjGl{BtVfVڠpXietbt]]tIm՗sTe:[z-Q|X{ɞՊώ`qPybeini`{}=KDd-񊔗Ep{9`~<{MzTywUeTrcPRyL[`/KzMrSzcyW_7a~Hp}TԠzԴojtI\{PWqVwvMiEvrGvԋ΢lғՋɒhj@jBcHysEch/^z/dr?j]x}FuHtni`wUyYgLj_bewo~{YKuT`iW~un:>4+4,]aZjVkWU^tkxahJeyT[l0EP4MRLkasBW`;Ub.Na/Ug?gtPpMiUm`vcfP`Paa8Y}*Ov-K_AYs=Qp0NfEdh}dh{J\D_bHUoMg`bauoatS^Io`vfcp`ifkryuyHLWTbzf|yՅrjmsejaln8vwWgZ[ye'l,F\+8/-6":4LAdL+Zt4:OIKJ^7&bP#^p2re?y=QV}]kok_7b\0XcAkJ;ah:huLbIf<٘U}o}ڱumufv`zf҈lωxzp|jxӱٸᎃ:ez#A:,3"U6qV#WhH\zH)kG>4'OHgd8K~G9X;5:$>@"UL Sr0j+^Z]w>XrDVm:[:Xg:Xu:[h5k^8s4h}WnLgD[IZpk^rTEtlxY>UmbkALl{LSUpِc[Khvjgjz|cRbmrHrFTkNp6g]a4wZx|RPdzgIjrۂmpJZehCrwQCךbpԶXghD]CGG&DN*Zb(J&LEcq|BknEtyFtdvҠܛvMn@p[[mcm{dFfT;GN-A^?enOlTsyߤÅ~omJfLhDdQRnJvjZr}KdpEzIdFUVzhxbxeqitccLlNdP}^{@Qw)Nx8Gh-K_6Pj;S9Pk=LWl՜Ưeh_]zhzdq~WOH0UT2[f@aiWF}dDrn1SJSN*_n/GDuhOsEY]FhqJDEYE(J^,ZTBgk=dEvSXr|XOdj6QuOXS;vT7ZB|lY~ESfma[vǠ{ʟvxjwb~`߅jnqx֧׌|Al>RF87+KE"Bs3?S,6B!T; Rq.7pO_FwYk\hSw@n[oF_RqTjWJhzv]Nqp^RO]DPS.aq8f}C}QcpgxZkcopTbnDr5yahPxCpTw}S{XXCx{4eIl@]veyd;|TSr^fR+72qQHyDO5.8);0@[ f0ǜw;YFQ)G&Bd*WM2ʾ\FUa[8P[4>I0F6hc0'aF0EN$C< \o9hmE{TerlKZreUb:lHDh@PLEW$Ti0my>q[FHw=sVP LAЬiBY.F_'t;pq[yYwPquN݈fkސNQhwaSumZtBU}Iy>o>YwMҋ՞þ戓U~}QgױӉ\Yg~TjH_hLt_zasS<\1\Z簆f]LxZpWs[⭤{]y}AskJ`vWRfiwYnaoyYwbPirZ~spUok>Mr5LCdAan=zkGaNxب᝜ۗڐyShO~{؁{bjq`u]i|P_u6E[3I]9\jOr_sf^sTxezm`~`qIh^`TC.Z5PTY6ll.eH]jFG[6EO,ea3T|>JP*ra6gFwb?|CQ[c]sIEUZ8ԱXkA`cYOpUj+`i@Xn=Xs>SV5JO/T.P}eyG`^MZVDC]2A`(l\ FuEzj3fvBZ]Ult;aLVITEZu3P>Py>Tp9ps?PcMeNC3pMՌ[H6@A(YFUP*E=jn#ԂQ읇ߠh昪dZoaapDXUR05[1<"AbAM2+8,HCZ`0H҃L6jb+FbCTk*Sd4p^(cZYANw;86%OW/`c2f0.-t'bbNftp.wwKTADK&Eh-m@f8m}FLs[A,_:*xM߅jEDgSfxec3rsnCQ\bO۵ܠhqCSj{HkYmRjvSRTrZםu|]gDXQ3z\S|ObiҟTeeL߁hJjuAWu?rziqof@wRW[9kOMzGsY|ZaisEqJjGhLsZ`wcgi~kkvN\w8_LnDg|Q}s9ijKjn╕Ǖƅp{uޔazțӌeKhqOf;C\6]m>jk=dxMzOkxNrPtiImvCqHj|]hsO~t>Yj>jwH}FgI]|GaW[hidwB^yCrTyLzRc]GPT6gY@nlggqYZhcu]x}HgVpgj[}uJklAW{^zgwZsRbKQS`ZgSi};H_=TsIjKUnJbs*$4&+EJVyNasDR^PnU\z]vIaO,bn;j}IlpUqrQ]{hnojo_FmyLkX\Nv2h}?DBWIkQ+|n;oH][^um[WAd`8w]6b=]bAYKNzO8i9Kpimtgcf=JYAi@]2WX0K{1a]W^/Tm@ga9:GL`;\\*;u/3L0PG(Z^$Lg/q2ZD'P(؆ngUB|<] WXQj3\0~iZdxctL>P6R$LX1cHet]~JZ-0h-*4W,žbgj^h"oFgUmFBwxYkATn0f:p;|@DUt?VK-X9m7Z݇߅y{lo{tPtBvy=yD'XH[Yntx׿~͎]owCtxEZ_lDr+D0"aU3PqgIT0\8-x~n`YoHp9vH|=JY0mKmYa`gJHJqa掗jxV8Bc(/8 G\,HM4Okd{XbH}]stPqjyZ^A_t=rLqqUDv8tLȉ̜t⊴^|PvRzOqیxgN\8cp^}y|XSrGk`v\nIsy]lY\}9EN*C^?bHbSoSt\pYqmVm~8Xq@bBaCXmCnV^JVg9[S?jxbza~SmMzNouNi{cna~Xqb9ZoHVj[tIarLo[weuht`yOX|@ExDN}DVhEPSaCIv=D{LaFVx7GJ*EJCdgP~mTo3bTMdBYROKCNQ93cF:BB=S+fW;mw=D\/glK;6LU XT9em8nh=cbi}BvJM}kNjqJyXEa<}Ke^ex_X@]s>gpSo|MwsRfJkIׅp}yjœmx|lUfObs}洠Ě߾uςβ֋fom\[l\GrXct._'3?'+N,Mk77fLS:YQ6Ib4eW)Lz=jlZcx9:m8kF5O5IQOG_+=e12[$:CAb%S])Mm+fo8RoCcU9hf4uC}Dt{ReWGXNjJV7QuBcz@U~@ev7h}EOe5cX7[a.zQ3hK]pQeYmk;_~9ybA@nOh.=k0>O$]n%`C_{FuDUT~[aVpMzWiaTUXv9Y{=Xk?mW5}NΗL򭁃ex\qPjZOeqYcUFpC/V*DH Ql&GC]6_5?B$Ќ>eif>3W$?T]!IdUZiay:cq;{Q/PO6KU3KC/z2juXJ@o7jQ!del%>1T{1Zq8\_0mmiQc9W\3n^8lmAdNTu}E^pAT{@nb8͋W{Zy+yGaqu7Q_nN`>zUcIK(\pjUWh{lcjmpm>N%-/4'.',2tM.㱀`pnyJhe>U^6W=!H9Hv2F\1^mgI4JkF\A2&g[R}VJb?OUfb9slDvnIxkPvLRW~F]|yXisPjvIgkDxmFio9kxZWeWhWGtX:J:Z:x]ok`Ɲs{eib^RmSvkvlqܯө۪ʪ㯖hW|szCAwW-@E1#Bgzf[WuXUIP?d~1Ro=~k:G{n~r_`h?wNkacW\oFJQ}bqJ[p8sx@|J֦O{Jgc+8dea|;O}_.9/EQ#Ug1yLR|\onBwf9pSaS)Qig`kIBohKhIY?-XD/yW.QyXT\zm3paCc`=N*텂aV?}AERai'Z]4bbr_aGSxCOw)<8.hLWwMIrCS<3h0Wi.Dړ]luRehi{BwwOn`{hv{^Q\RCB\WVATo+hb%pk(U@uR(xA0T6jjA`?rZsyN^u@bi@hF|KsGXgPWCgv:_g7`Y%;M%fk-gUTpzu^fuΕzJNXR6׶egn(JK(Rލ:W(]V`INU>fD7imGrinVfqxR~R_e_sTx_jgDd{@vMkB{gXj:˞vcZ/nξ𶖾~ZCdl<_SY۽eoZOVqޘn\L}fR}dEq[jd@j@k:Y,j8v:Is/Hk/yh}{|{}vvYjڌuL:[*_a/Zy>cPdzHoSkuMwMngrOPk@[O8U`6hchtqylwkTptsAKpOkq@NlEVw6Pu8ds9CP+TnE`r6}vGC`qE`xHvZRm{S~dtRvrBblG`kZjK`KnwSgWQqyjokw@{dX`X}{GV^K|^rsItqc~sydn}Lt|3Tt(]y-Ob?_x?fI~o{Ɂ΃YqUkrNhTcyEWiJvvkKXYqsjwQe}KgmY}Pw_dUoQZwVh`eluk{eo`nSkF_Lgv?Vc1QX7V^HyrKehInd;dP-Q:+;.@)%S=`O-g@xM~h\fE}g]Qgu4GP\dTeq;uq?cIuFlkg[tNerLt?|MTNeKeT_^/n^5Yn;Q_;POEIDR<@"uAA=7:E6?)G?$S^(1lN?@"F`"Ud%Zv;_c1kc1SzGpw<_JvYg]g^ybuiRefqA=Uelp7lOK/qb4byjY?gdA\FIևdNOT-Ji%bo'M^ZL*`X,UT-zB]o{GWz:kGQ?>^%>U#Zk,e3`>df~|sw}`Yg[@NLA`wS}g?mOzQwTph¡xqrXzdEkzEhjM[{RZ@zS9Ninckw*U>2~e^\3R[@^acBliQx]smnIN\*Yl4]g7c|=~fי~ͅZm`T~L߉ozCikk^Xo+HM$HU#L\>fr5UPH~HgwUg|>lcAue=T{Jumk?ZEhYqZLJb4TqhäpcdXzz{vRs|`zZoxHhvr@XtTl=XlNpj}{_sOas(Ga/BG,A`FaEaw:hZdqzentxf{LeQq~_YipD\aswWtUllkjsipfltkg[[ViViK`h9\\|zGxU_`P{fi[Mr}9RxNRoTffCco:XH}eAglYftL~qIjvApTsPW|QSqQSi2SW4M]0XQ0J_5I]F\N3R[8[[BZK0YG5XE-D+h5Xڈqљs~~qbaNgNpP]i篁Πǚ{jj}{ĈʭoyZy֘ԩ`,8X*!)_?6q3?@'*((1 2&&/*:0->:%?T&AD3VE%??'1I)>=.>TPH&gE!JP3+PP)31-oA6y5Fl$Ti*c];T&Nj%g?.V3>C#lkGzdySzRfEaboD;M1WoSc=oi>qisW9G*@*b;\ZR6cDV(J1fgJh\{?X`2`nDqPukLeZGmqaxg{VbkbËm\[6vF_TtoDƒuTTXh.fyFt}˝˶_lVx\ek9jOgrVzւaLeFLN5WLs^AvT>dj>u[kQhhì{[J`S8Sy?9GF|9Sm6Fd3e\;zhKNlCygNȳۀUPj~\miZmtD\a?_;kQm\eIQl9b~D]guNbi9`|?kv:`~;V`Bgkz|gSxk[r^`yO㰳]`bEisZnfmc0kaEdz@\wJgvSe{Wou]}~HYv7MiGm@2`J^@:cK(Yy=PrL`fGmh5li>h_?]kCRnFjd@d\8giAW{I\UFOc2Cd??R5CW-uI.th/{}MwQbI^pQVYDZS=SU1fN/].b=WޒsԐpbqZ^joǔ҈phrfbQӘ͉}w|mayD .YMOi)P>-3&5 !'#$2+#79cP"qFTZ=Z@DF(IH.PU2Mg5bS>Ua5ffACDNbCMi8rm5KwIkd?3sP-A6';D63]$R=#Jv+]P1pI(RlAMm>Hi,fr3CZҊwȇroicZ}VjuDi?Lo`h<}9pf~^`qhlmGqj>LjXI@8_Y*9[9GB,bK#mv:SihHT^Gbl1=c25F'H>R"z?͟~o[n^TpKjGZlD0H9/=".9"24;A=L&2M.GQ!PZqHYpG1f2DH Sv/hg3NwIlbi\lTCOo0Js.1L68ieaONrܪHMKd DI"w~AnU\sKikYzON`qvJZZ5sgD|TqcdvmKxTid;xI5~ADr<|=q>\KxYmv^^JѰrExNvn[d{c}jrs@\fvTKhGco{7=3"]Q;e?XaUmSo^Udz=MY7QHEphGNX$94'RO7Ze2ET+JK/MG4\ewcu3JP0\W5Xk6`s[rukmrnrvpuztzy}ϴظl[iytYpIdyRSwmJHq}DsPLmYlW[~LXwJ\vVj}ֻrwKgnRRQmzEdyRU}NoIax,Cb0Af8@g=OtBNrdrtux|SpOpwKjbF`lQeW`\ufzYl^ofdL_y?^u@_kA_SicsZVeKm`C\In[;~3JqVK],m_E:GMO-;_@=J=TE)T['c^/XiGUgJd`;ToʛO^žjv}m`rjƜd{Ly]BK !' `7({662;$=""='BRK]$lf,hAlSPwJYX:lU+jx\-ii-hEX|KWlGzy6xZzmjrx}jqpqmrNJxjSN8y0cY_>?SGU&UC*M_:LwMQl7PM1OO0bk=b{>U_MGA+T?I5UfCe_-Aq>w;*K{td?[l_L]Upa4wJGO>x@Bs>@v8@x0F[&Nu-X_+^o8vO0FiH\+@a3GS%be0]IoJ+dK`6ӗ^RSl>[6U|JPb7~Eh\ʆZqg]{jFvSO{|mzYn@WO~Qxoq}@hS\'Ko.ai9|M^dAb4mORUtANs~lyOYrIXF^7qfMevKcCrM䠬S|SCoGe4kxObIYAVyCqtAQl%br5jCfD_N_TՋJx@ő}^waPfnYVHUU]JOHnR{C~|GG?uz5Arm@PhZuºwb޿xXMxGiQvKczmTvWpkgu|;X~1Xw@fy4XJ=}NxCrMtHkL{sjwc)dnJ\M_S]{<]~J`|\jzQ\_Heq8EO,BT+AD,Tf5HTSDJH9O`/dd0P]u\5nn@G\]sVHsE8nVHG?HM+NV2bd8Nu;KqL^_@Um-N_:[a8Ys6GDN^GEZ7BR6GU0M]+[S,JY0Pd>_Y5k]5htGagJvjDK[^i^r^Nj=Pf>~W7p/Poa]rvT`f{wz_{PUbZɒXrC{tJTdrZwbtO_XwzT_CIZahLkiH"yH 9!M)_q%8mO7G6;/$"77"IA`X)ox9sEX]n\rZbmAhzCfq;ekAUyVmX>N_9SD1nA&T.at_e]pbAkb8UaSOP6LY3YH'cw;Ts7k=RPTsUZ`8VR3q)ge^xizxqwnti^nR+aXF,}i$]aE=2HBMI&TQ6eX3]g@JsFOeHHelOa[\W:HX3_S-[{2^aAMh7S\?H~|uAP^jQ1gK8AI9A)QH,ow9T~҄bf]|JLqPRY2ZI|9DJWA&:U+ET,[n>d?LP4d1yvͳ]fqcRcbQ4Ma?|y9NQ[zYaAqfRLWMGG/}kp[{^tu_zqGtH9N|ubYK}Nb[&XguMgjBCdELWt?jw9xTGUFH*eH%xk>_opN5yD8FP~\Kc5g/eT^{@dzJov:VOqd,ii~PgdF}[pU|QtV]zt~~ԄڍfiE|GU_3yu2n_t4MzAhxXs-Sq9kEy֏aP~^zYSwzT{ϴ`]BwS>SG/[94SJ8yuOjB}IWg_0|tKOsvKpX_]]@u_|f`Ʃmb[IuubpWp{xC}Pu[xqJ}~LrW`uUXQ~Zll{vpkZhPu}MvKP[b<\]LrJ{\dnf^bfes]zaՙftoͫfQ}Zgk^qNhuKkwartҚk_{QtzѴٸsz]]F[uO_~Z}kÙgUOtMoIqNlJbq6K]-D^+CQ:Vh8Nu:Nz\\xx]ngxarRqo{hieVx[UwrGfsYwizcv`rbzRh{JhTrWvIfiS4J,HH#aR$Ls6BaoASVJkGWj:Gu8>c?MW0\Z-Sd3~d?ls6jyRnlL}aBe9zGoWzycguQ\pQ{cIo>|NkXiRkIfEqJbvz{dxy\Qf=J]Rf[Nv>lģbΠVnXuZWrYUvBZm9Un9X0e)hyV>mLeO3as:)U,80#^>A|8DUO*U/.)0!FEDV:G'gEpqeMq_v~igzVi{WrCqUV[b`IM[5FW8MF-I*n/`zk_ONL4cB1fpGhi>2Z9c7!\9OlXӊ]@w-;SG;s_&|Skmdag^QDvcigStYJgjLVN{J_HB|jeA\X2-(2-!HC#xBc?}jЙ`UԄȉS~dsYR[BO}:gpByw>TkV/fA;ϿudҲeK{uGoPVq5f7{qWiO^=͗syxjkQRJEaD,IozT?xTpwLnMxR/zPEdwhXiLLrU8nl[[B}Lt⒠{;QF7fVPP8WT4yyDoKbcbAA^|yx^NjFjx4a8rlTf"F['abJ~QwcSoy6KR1RcAb}Pb|~Ҹamuvbyq{ryc~_v[rrTykFob7`vHaRiYiL]vZv}S~yUrJ`tMaxKhh>[\OBS(@M(VG)lV*[HcAD8KKCC#it/TFP|S:o=+Q5A8!OW"FP=[4PmwYxzXCVUX:8d0lO`~YbsEnaBfS9^S7XR2mS=f4\wGHZ\O='cE&Ih1>d=59%gT(=~jV\y@2=OHB]z2kPaER[Jg7GM3YG#OL0=U0I`/OW)RdCLZ6wd3TXYZSLJq4AF&NA'sd9UWPQ>WY>GfNZY/nkHL]^xC\>zDSwlZlpzGfliChM@4:=,`4qb}LWRWSOAԜGdxWy\gu8N1Je*WQ3Kt_CVMrZwjixT|pFWnqNc`UopANwE}\zԂfMGs`gyM]۬vwVIdY1sXTYfi;hmJpm@qanaLs{sGq~EFdyTdtHFf0Xk&p3wPdHSdl-[sqL|~KdapfSuLS*S_>x2M_.|^>ty>EOe|VmIfWmK\u9QKee2Xv3:_+5h8/08K'WlD=tJvpGcsWWk3g\ų~[kSPLt(tOAs@X5"gRuE)qC߹}^\vpIw0~l3pFpOjL[sǫvci{M\`6wgCe^@vrO}M\KweQw}GljC^X:[EzqYPWmkLedPcP`xs^x^xed~ITʣ\pcPxSuMtJmBW9[V]pPlE}Eg{IS`w7_b9kRIm}K[Ûr罅zY`z|eĥmxfK[i`jImXwuwoqrkiqӣŎbxtmh\Y}\D]xDbQ|hlaOd:P[1U_E`j_[Grr4EV9]~h_lSil{px^kgv_n_uV}~Jtq>qp9XkQgXjP_UdQcLdnG_xFeK]AVDfzA;F3BL0SC-^Q)}]2`HTdR_WDLqX>[j^g`9nw@K}RP\G;h=:S7ZE0VBw\zJkxp]|9poA>>UE)pk;suJ_RfFMfFPJ9nK+ih7sg?l?ygFMi[bG6HF1VQ-Eg2TZ38ZUpLSDfKVErQs9h^pUHj3G,(? 586=I@Kf1me?]nEq[IrJPOTbC@w4IQ1tk7NzTDkJEG/c/i|PxLxbqnlZWm^ANw|gXh{acUn]Uv{a6a[HmHeIjX`j‰gċrpXJMM;]]&buBQv{oTˇq|i[u2>T#Y5rZMi8pEO[Wf,mboDQy@Fe*Sc0Xn2\m\U8Hb(H;&mF5jvFsZep{aw|SySzRr]njOɋbˑe~c쑹fZaQTWTxqBW}wO}XkxQþ流Uus}dzYdgzQbNgNv`UrBWB^@kBjPNu{6ooA]q}Hd^a{aŚ{htg_lr>`v>zs:ypGmGcx:akTz^_V?i8Vw=]v4Lr4q|?dKtYzƎcavFyHIQpOxOgHO`DQwS|z|Ɛljdxİϧ߮ʄ̍苞|r_g]LrMiKcFhIfhZwDRfF.NG#Fc,N;,OW'J]1LC)S:&[>%Q_8>a;E82vB${1V~ߋqpNi>UDLX0mb)kAcZbMrL=N,NML9!Qn(J\9Ze8YuFFPxqP`VUUWdh;u_mog`PuasXBmAbwU_iSfmChEjtHxo=`FyyBltIZ;inKLiNVaHZl<_o9IXd\xTl6gSiTnV9TF>-\P%i+6f;K?BT"1T6=D.SIOU4[n=Wf&nfUi{Ðg}syZfv^coÑt;N?>)_S3H\roa:Euw=qKT\'oRko|KwSaiRxt9}HgƗګyzohxwwfk^erMˁU|H>qbeWNk=rN)~GVUzWeIkhYZYk]gOXWQe(>Ys={qvERXo`zh@fpBdNl=\N(KI'kq=hfWOWoC.sVi^]vHX=ƩιFQ.>D)`A+eAV:הkԕ^[mؙ웾ߝv|Wz`|B}A͗h{gguTmL‰shXb}UwG_ϑu[q5an;yj?iy@\^8uAUt@mvNbjFUBT[4}pQuU|zFojolJdyOtRw钛|hbŠywwaar\OebSKdiaa;rmLomՑ]|_lSnEapEm|YUh3cwCuGj@lAjLwJYVt=]|B]PLpJ`PwbzX``[_H{ct[tUzMaz9]l?{{}k|No\Uw=d{9[3X|VbGUaCrcE|IMQB`S,ZO+QW6OS9`]3td1i"qR0?o>&68i"RKv~ubj5gZ!I84*KGh^#Oo:ZSBds=xiAEQ0eJ]K&A|4RV?ns5WjXjL]_cC8eg8\{PhXLgH\de|h\FS`=gI;fc8gnKy_;t|I~|NsG[oohf@aLtc:ZCUFU.z:n|smn:j{@`zK~AwWXrZst[rPKrWmzgvpKgNS7iUJByS-ozHnK]JUkdM\ИrrKjUj_vZhm;lq1^CcpCOMs7p~Mbf+_f9bb0UV#yh6v?XnFXm/CXU}O<æzrRAP.ptQWu3m_clIlq8k_r~uNk7]t@@9L_&]b6QwPtDKg;Iz{PtWaYHöʨuhel8kXcx@ySGn8RQ%yrN@:#TO,ǗcNd}`eӳۓ{ƪkjȕrںo߯xbZ}Xk쇖T{Iz|KY^/X~V͕i~nUjQwokmInTΈ_II'YP,ptBrzNcxTbf8kfAiQ\rAxVSiU6XL,XM/j{I5;7/=6"_ZC{N]luKԄQm`=eeDlRugMvY_֪`ytU[UOkc]O~yGaw[xV|Jbf]\uMwHxJ}EhC`~QbB`wEky:Lu0\l8J}_mKkGxBqJQDjVrthrLnNVnvFZY;VW.ZiB]v΀dX_2t|BqtOLn]]l>jCa~;b}N}TafTdFrOwZc\lWd[v]hc~\loAVjsNP{ZZtU|Uk=dtBYjJh~[rUrpIlxD`rTdnShjQl[kH\{CBh>X@^s@aoG\teus|\kMY|QUXDb^BOuEicGd;u|=nmUUfPW\Aj[>l_?irIamTknQp{NqQH~[KZPTP-TM!ZK&\T-n^8}^:ee8y`:uYI&UL%OI/XE.^X/XM4QW3ET2OP0QV*OQ'K]/OU1pY-f1RS?pd@P5TO-Y_4Uj@7xG&S<80K=NU'OS(PC(pA,iV3FmDD5L%SJvrQc*cL3<%%P):#B9<[#)?%S*Ot%Q]9Ng/Mz<_{ITLplH]u|XoqUkJRN@nL/]}?ImO[K7ea9W8Jpɓu`bVeDfL:e[7\lBkZ;nW9rzKsXiV\|Q]BOtGNZ:Pd;jDqSn]|Nidqneib7fW:?,tG%c@Lvl>C&fyKOKaFXR(K|X_SlL<{Sd_zOWqŎIsVV8Ic5VU#GW+{/ToAUp8}Ffob{|DjJr{s~hRTgn-gZa_;RevaeM[nMd8sZ:ha>}MqwU_jyBY{P\|aX1eFq{sYxD¯܍Pv|FyZ~\cC>u:PY,CG)XV1z]7w_]XkٰՍ˦jkCc|Yxԗjݤ|jF\n[xi?ɺ[rNZ|`UĆO|ai`jRӁg~U|g–zawPel@`Jרlm|AdZ4buLdQlt@~x;uf4^L8OJ1jO>QC+6;3< BD(@L&gd@~jU2OWs3lHieok̉{^[^_{aKI`e`V_ցZ˒gS[cnT^{cdx?qOoFmQZWugkBPr0a7Up4XkAvTvC`y@X~6cl5{Si]d}mhXW|Mx~Gj|Md[֭|̶fxShIbF]_fV}o}À}OlKk}9`GbdZXq]Xxt}lhscf|UeK_~>Qj8Xk>NkDazhjxĖuBZq?hO`p?_[Rdigfirvqk^jfzϣ镛횽]uAe}[iCNs?WgGIeBAS>AU5GG$XBJU)WS5`T1]S2I0l5oUSvdC^D>T.AL,?@*KB,Q\/]]9ik5Rm??^EBT3]M-`].Uh5jq<~9bJg{YuzIOYrZDa0dV&>&2(>8IS$XS'mS.ga4Kh;0T/GK6W'Ist9zc;DC0F"6N"6L$fMZkR^[4LpATA3Z2@y^n^eVKolLbQfF\odrs~{Ua\WFivr|tmgtX|I{^Z{VXZ@7T76<1@B iW4vACe^4?l6\_+ve/xEudZtVrF[wM]{L:tH68(oA#oKra`\fZMuYlNrzMQeXmGJj6cg:Mk7PS5Pl7^b;T^FTg\q2F6(|AiV@zXɈg{eZ];D_nRLgHCl-Jb5IW5Rb/Th3^_\Oc~EL~-j8rWjŌƔ[alv]iN^fexJeiIai_eâqMLPeNm}S~PDMs.Yw:[t7[C}KgBbj4Rc,^p4VyMrYpQdj3w\wHMs2qyJS`Nw~cܡ[de^U_wX{WµqUc}EA^Cpֆ\sľrXes9ZwE|WfQsKtml`WRz]cj~eiF[}>S`:Ta8X`Ce~Qf{RMlSw}aa|Ja7RWwbgzRg윹nampdjQYae~~K]pB\o8Ui9_i>b_Apt:Ra7_nA]xHp}UuR]sQ\\m^sjxiwbw}KaxPh}TioG^i^}GhQeG\{GsH1Sk@Cg]DJIGQ5dX3bb1bp>sm;c~CQuRL\BVZ8bb:OgAUYC[c>LuH0gE4C)89:HR?'YN%HX/vF4rw1jIW`PZYAYi8Oa:PZ;^M4ge7w~?q~H[UIkVAY=ZG'x\,vy4fOYrFOZ;ZS4dX4|b;pAfKKZ0fK&K2$+A*GMFT(`G NR&CX/EO%Sd-}r9byGHx\0S968'E;DR"4^6?;5U$4R"4W(G\*Rm9Qp?s?TtnGs3`hHD4Pg,Qh2uU2n7`X\^Ph]1VnJYA5M[6B2Ed]OCtV}Yh|bovLugj`>sz8kOxarimZrV~cbXN?fGNB<],Gc8Il9lzDP_nPkPXe1@^5F@*nH f/lyJfPZsPftN=yM_P3Zk:}xOua^hr_KgDYmPe\6yLaB'^s0]5U`@t=G|PMT4Xb4bDg\vSQh{HCn\DUIt.˵YɄٞs[rvZrYQp8zn@;K>U)AJuj.jTgT@lS6`k?nCqϙܒkXe^H]R>be:gBd\JI.ZUFie0nd8T_hLwLlp=]T7ZP1Z?)s.l{aEX86U0wNamf4]m:OvllU[Buݙ։M\rĤt]jDu]BrfDg`7yK4iu4zP[PoHkOcxDoHh{wğ^kA`}`pJh6w_9k~UkߧqWye=}ˀoN^ɼh|Ng{FvSt]fft:_{>]?wN\w>frB`xB]9Uw6nHe[^MrzTx^kGa~HzxCVCW|ձx_zbxsT`ݲx~ʢcg|`RmUW`Bh}DgHr|r|egRwzEht4b}=z}_sSlQfi[tIiK`iATfIsWal{EYn?Ql?^|I\Slg|xSe`~{߀e~JcAkqipowZq5^oAf@PU9cL:L4H? 2M!24078!DH$]Q(`h+br:]rIehEog;mq<_zIcn?ls:KrNYx]C]L;R?UK'_P%b].Lg?KL3UI*M`-QT1O_4nW3\d,MoJDeD5fC6J+ID$DY1iS)f/zX|Xu}\Td}]}`gejYcXX}Ql}CwEZ]GjSWh=[d?peC{O^Rd^UoG`VUpFMVJTCW;IR/2=%+6 -.s4u3WvOLG;c_1ZzJiAUjdz\]zm~LiNw]dbjaj^ePi~CjoBPrNkgfY\Pd\iowt^p{E[rKw~yՔ􂦴PZd:@`:7V8F^Gt^YoLiUQly<_yPzolSeDb{:Yr6Qm9\]:\Q6[K;RWDUxNs_}tOenRn]ZzR_~YpxWgydo~gkjp]m~E]q@`kGgw;\@YPpashUcK$klAebQYRG[_>viPal@ZkN[Z>Ih>VL=UM0MV1RO1aT,f6_FTtOdR>^9OE,AY+KA2W@)TW-Qc7Zb:ko5Tw=JiBU^<`a3\x;nrW}|N}P_uWNgK6[;KG9ZB(DJ*9H-KA'MH!ZG(kJ-Wh8J_68X8RP,^Q$Ub7FmCCeC9J07B1DI.HE*PY+Vy<[v>Ta6?zAQf7Fb8ET9i\0Wt0U*LG q*:KyP0wDKiVX1]tLUa;P@3UU1L/iNSpTRp@Y0Zǐjr`V~dPPߋL{[Jc$dp0tp9FZQo4klCqQ:H|W^RS;RUw[/_n)YC\Z(Yv/[GΠo[O}iS~jliSuleq@sƑfky\i\kT>tSgq_n8niDXrZq9q`3rE|v?Gr^sQtRU{>qt:qPck֙dtxҕϧvtȏ^hrbd}CahsPwVfyOiɂ➵ꌧivPZ\TGڔgH~}GgpGqYwdˆtݩ~xc~]|mk䥎fҏ}A~vV{zg^rzJrNZ|Aڢw[YGQQuLgn=ge?k~DR˝cZq觓\_zGV^0On7Y^p[[i|KwnDpsiWjOv|TeحʶltF]TQON\\UxKSm3OnJXm`LeTitzSrVڭȹ{̦mgZ`q8tNsGqViNZ{:Ru7Op;h{RsrS}u>rHmEs|P}YzX[WtR}jXSUAAocud{jhaa~gstZ{`lĎvj}_oa|ࡽނg~\vwdn|Έ~trЅkmWS`UiF`}?Z|EXvR\=UtEopCqlPZHimMpv՞ᐔmx`mGarDafC_tF]I`QmdtXl}P\^$We9JGFXCGiT6vIvhTemOUxNBmPRDBI?)VF,QW*k^-c.cLKB^YSO1]K'OR.XZ5dT._X+NV9RC.PU7R_3U_8me9qp>zNfUV^SPPBRP1EY*P_0J`0RC04;*42(KAjAcO%Td3R\FJ/ES5S]2Mi4Rf7]g'xp;H\}]mFTtttqshrtM}cNPB_`)Tt1[m:OoDgpIRyHM~LLrMeX/E]?WR7db-Ym9^X4jd4eL`UQM_oKa]?:v=;606T+6+2,$2)u@rBjs8^kCSsB{vBgmhRN>vML@4_V.2}CRF%d(Ea:W;Q] k+Ux\Ni2v9ZLSgIae.8rFCC-TJ(X*bJeu]TMuKѠ_y}oxM~K~Iu~WCDh;Q(BS&`P(Ms=jT:\9SYE[=vl(]R?5QtQlr)btBuj>\StiAbYNl?bpERd>smFfgDLRFTX6gO-plIp[AUU]V2\L.V8t]iSO?O:T6$Y9v[QrC[b,B*TePk(h|FQAzn2WrIHc^Tdj7yQg]?V]vT[qJOzAlfAhjM,deP`|RlExodHI}ZmA6<&U@U{.a\3aQ[\Btc?;7(a>gaKjnKUӢs_j9noYzZgR|WrImq_naT}m8pV]ZmvPOxxB{}Qel9kuCgEjs2vk7V|HwZs8xu9[m@xEdzxσot֖}ꣀv}jgmTWciHKR}Rq܍fLlStJVn?Mm=xRkGTwoOb|crɳqȐ[nzt׹ۘsGld~o]qLa@ksG㷱vczKzMPY`PtKw>JzEjoBn[kVQksCtThh|P}M\WnBlhEuMd~Hsf?wI~WpyJ_bEooUƜY}A_Thbdt@hyړhKRUjѻiokzoonwBtR{K{a`bcKkWfn`j`KjIfKc__sJxwOxaxpU{A\}9_WamVnLSp9N_?KhMtӉjܝlng~f\|NpvsuUze쫶ᘞǕكLjbwLvPhuLaN\~GUu.;I:GdBUoKtOts=R\[rR|OGZ}@@wTALCLE/uM-kb0ioxciqKnM]GM]8OL+Qo;Gv@Ql=P#~VrKtWWUkCfrVWZJp}Wsae]g\QBeCVN/VvLUy)Ut5Sh=Zq+W{>hTaTXI|2OdIipR`W@jk>j}ImpHwX>~iFS~{oYlUriY[gLcMy\|lhuP]8_T/Av\akӂR{܎cttLY}QjVzs|eYpxLgU4g[;wJхx\tU}JpEׅdmlsTsxPmrDcpD~Re?k6Iˈjm_?axZu\lxa@hwGvJegouCl|N|ֳŸhH͠xeGrwL|{T\;Ih8OM1g@-bC6`Hc`SXSxIyiESvJxHT`E`gQO@oZ7by?ZoR\XFeP7nT5V^5bW7_4VX4CKYLC!WK"U?#HC,ZL2j\._Y2S^BPT?]L7sJ6ld?ikK[_EGF;1=267$G7!qK"n,`vGFtR:^F.>)78:E@C d7$QG(TR0SX/qW4eh:FsM>l=JQ0rG'u4rЗcQx_&?\)4P$.=%K@*BV*6E.)B"82L;HP*Fi3NT+ic,a]ib|qMmnLTuJXeag8fi=cJ`ii]YfKrSBoHHI4KQ.@hC=\6Ld-Pi3@e5O]1Ah8LbDQu58^E>N%;@$A:$5'<@!:A(8E,UR*S\.uz=VG;R/8O,^Q)OFBm:8U$?DVQyj*RsJgV+ggDJnA~U(sRr_mUw^{}Zy|Z}ceyzoq_n`IK5LG#tQ.lbLmC{lJ]uv\IF@Ya.Kd'][%Xn;Zu:Vg*n|Fdg@ll=qCXtK]]L(R?dpPWzȫmwTYWgǖdLNc;g6z?lLoiPqT=cMzO>x~JreCJdD=jV{ZLieQ[uBdvqnboesFLojEUlQjU{PZs=Vf3l@d_[C\_|mE[z\Wx{[csV[{nli]`o雃q[l~}Rn{<_f9^r*Dʬr}ru?zS~~M|R_VuIZ}`}SfOa{Xo[k͠ĜtaS^?g`ȫ`ZnfAC#8+N-%RB/a];aI6RV:`[?qːmQIJLu?vCXyHqfzL|hBiHmxL`dgh_cWSbuYf\gp?iU=pQBv[zGyBq~=Uf4dnLĕ۔}Z~tYp{Y}vL|Jg\sSvXwxG}YqUivImwLj~YWo{bjYvFp\i[^~FxoS~RhLrSsa^hm{Ăʨ{mmXQkNni\hg|RewAc}PrjbyY`Ka~WpaRgCcyDk}Wc}xlmZ`|]ySwZb~jefdo_jcEGUChLS~XaJU]7YX5SiK~gyZpNjUqUwZiqKRW,?9+8D/EL6ZP8V_?`qD[~BSpDRlIHjUYoyXdERybtc|MgJl^`yOkzAUs@EmGPwNdVB_&j[9_s9[sHDpEMW=eH+il3ng;ZxKOTP>J:EH)G>*ZG(T+cIMDbX?N0XL,SU"\U6hZ4oh;llCc`K]]Fg[6g?SSnXAIJ67+:-K4!QH$aY'nh8VnCLaK5dB0D057AHCE=C*D=+NH'HJ0b^7\p1Nw>Gx=DlA`S/t1bsWtLa?oS6N881,G@B\+N]4eS(Sh>AUD:F,9G'@K&UD'?_6>G6M[&^g.Pu5@]8TC';I*AOCOV-ed8dn=S{NUZMZc8ixMCm_X[ogh`qH}rCNcVlpRll=Ug>eo;reO~KE;=H*L>#ZB#gX1Ow@QoBk0~N=N;>.AU.BK4JR(Lb4T`=Rj8\|3P@[d8Pc-KU0Pv.o{I`Cqrb4bWmtNt@X\yxYQeqTG;]:3I#^YMd.r7tvIOL.I6=18G 51!:46(W[`j4IQX`:;u6Nd.Ti%Ha#/Y9BMQjn2GgZkg4YTL\>gQ3zc;pR=lU?ieABZ=eR#Xi?hS5DL6}h;xoQW`AG3:%j_:J|wwuQmZN^EOk>Id/EV"?Y$8S"{Y&<=EX/Ti>bpAV?]NNkDKL)h=+uJҊVgmauff^l\_jrLxrueh\}kMgUkRfhdr^kuTj6n}CQw@^dAh^[q=SxoImsGzHfs[xv[yLmTjڏntKvWďl_TXeGWo:Tr&;P]jmJxxKtFrrPp|Ao<_Dnc>_W;oV:_6hM9ZuCjI9uW~jζtViD_Ii:4kN]{h|OL;GEO[Y^dMoE]pPypxzVcdz~cfS[{~MW{˄^wGz´cLtIP]Ooqnb\}Ub\mHwVppf{?s}=]Q>mC_vBeԙ̗tʆmoUnĆgbv:luCkosOUsQdmP{iǵmkdLyPxsi[N}U{agQnBg~L[Qbcg݋lqqcQb^paxRZPZIVVrioihT_\h^cFVCw\MOyϼﬖzjy~`odsuYPgX\mkwvfh?8f12^12b=EeNi}?Vf?gSzHqKe]nFO[@ESAIlAQ[:ghOnookaYceaHO}Sl\t?cr>iH^wB[yCMn>QwOZJd}Le{B;b0OQ>TV4W[5K`?PV7cK0`j2lh>EwMQ/FU"OW%^`1bf?XpDWKRzCiF+>C+8M*J3 JR(@]D_`,W~-Rl5=r8ZS+bi4FgQEJ8K^-^N,Ai4R[B[\/foGkCaFw=Lv}kZW|RfxM@nNYC>og2T?X^?Vb2XO8YJ6Jk;=K4Q<,`P,_~DPkQSK.=I8OJ&FV:PL-k`3YZ2vi7c{ALe@Pq9YwBWqN6lQGXYNP)`T6fX=QwJ`{Ww9blso\6vH9A&

d(5P7JT#YgEnCUY޺pIlghgYiOY]>yS1zo7kF6E?wP&j\_M3b|\p3)lFk^lM6d1QؘqNsMdgRPxLNk6H]3Zk*yfEN[9\l5Pv3mq9^}RX@sj=a|QSh?vC|WG>Kc\!cX1^R,Hs[nvFwx{h~h;lmsoP~SksVysuP|`eMzzGnkmAuX‡ew_dUbK]hCkX{Ik}SG]rRKY'czbvTĜń̐jcdh[wQ[b@nVui@SO/qFSI3JD/aB1__l\nD_vN\sarVb?z`L5kPrRyYrR4O\(Ly^vB[Y5`a6YQ5cEkK|WM_n<_j@kHU;[pHbJdwBWuD[RgFQq4FX:3\94J7@?.BC*8G#J>*WM%nf2l}6JwRPIFab9=y;=HAeC*`/pRVRglRUBL_9d_4eu.`qF\g@ZcD`T8b9‚?rm\rgSWO7T:%?.& 5%Q8cc$I=ZgIRY9~_:E^pTQ^BXd7mZ,ob/tF}Vq]teTe[ebYyoBTfhȂZ:o;C/"VB:W5`N0Qx.Yi@S?k?h@He]JVGB`8;E.AC$J\1\R0ReM1[H*oh.mN`WGvOEZ>Oa4JX0QK8R\2BA*K3+G>-_S+f}>D`O4bI\jVWiGihFLoELI0K]$DU'evDOze=}cL?#w9_nPlCQU9Cn=UF2n{8emPcnWhu@w@Rj*ZIAC JY*b{.FgFZl'e<2U<"2"DHK"@@%Lc$g]8KoCc?1`Y3fk?OM\cNJu@Ea'9\"BJ?T,OU)rt1ÌI\d[V\AaJ'K> `X)uw4LO*_M+P%5uc~bTkdOgbbPxt=tN[UыuPwJoSRU/Za4]q2_LIZ=dO8T0=Q.?R/9R06B+8C(YB#Ur"`rBa|9[]@\J0\I6Fe<AI)DQ-AE1FO(Ia-Qn1Yl-dw>jr=ZNk|PRMXPD}=cS[L>Mm6AS7>>%8@%TG(SV)W\;DX:YG8fd8cFe}AbBHrVJd=1V%VS!BY0GV8:G*ZL,Gc5HQ9BM/?X.RR5ZR/SP5HM7H^.T_2HX0[c3n@_aPs[o`=M:WaBM>JP*\v}G]i^H=)pE,g^?h>XazdcQ6pY6;T6huWpIiOz`qtٍditPlGcPOI,>H(D<SI$OI+nkgkC]b?b2qc@zwLak8STp8DtWm:=_2po2XgpXu۲zp_hxjj]fXF|hJaI|aɩК{יŖKeD^YBUEeQo{B_gGe<`yII4heFrK7l4,^hNQj;[J0Vs@SgCpGhbhΪTLrKUyLrv>xkAak=[ibיI}Nx>q͗lSlSo~W:vEgQm}=sшČƠrvWNy7:>'fF3Y{hkJi~PflLX_CEQ1fdUr壩ڍۥs\f7ZHHh[|P~cfopˏoi[iqpVp~P^xK[z<^HniȕɲjilFf[]n{oŊui\s]{qdϝ֊ƌuV}Zҳ돁ctXcOp]syq|X_IkzElpYbxcy^mduwiQt^p^^[dqavuͧⱲ氧߉f]3Bb2S}Wx\fJj^}jv|7LsEaNl_|p}Ҧ|yurhxkU\|MZzLVvIQe=K\.Cn>\@[tE_~Jo~HevF-M4F)1T-2S0DO0FR/CW5ac0_h-[s>Tn:Bd6CB3II)OV.R0hN:GQ`PM*I_.Df9d\:Lt3RaDY[9v`6Y=w]=o^<^S?HN=[88hD([g4g^;cf9ut@LGPfT`G3Y1;^?UlQM8FU69UB3BE$,P-0=*I>E`+NX*S]3Yh3j:cGV_NsO}T[`uTGf[5N9=*JZ!Bb37p2R)AXx<.h\,KhHct?]s?R{I_`6`9`ju+1Z2/I$MV"Ab,?Q1gB/SL6[q7ubKYA`Bw}i[_R|=Ad3aR$IS985(S0"SBpA~rfxQA?Uc>K?0sI.oUDOgͨY;XgDo^>~sGvYhᷠ]|[rri5`oDM3nPPT^-LE'`N3P[7EB%`H*CY:SN5nxFOgG{G[mVBDŽNfn_:p>3/Cb2XK*Be9Y`+Uk:Zz7]RLp;k8grlz{@rU׋בrV^fpmXR[p\uo[XMAχjİ풊쥈rFƿݛmfKf8XK6f8&y^C\Ah]Ǔ_Zyb{Ԑf|Ch?xGGqiˆqpGX̎R}YN}@th=zqxSbGvsGa䛼uVj{^əuHS9HG6EO1LQ+xkϼ}㄰\NT֘Ф~[ënܳai[cR_=dz?R}JYsOw;pBwItfGn{9qC]{6u`Eh=~Hdw2d|9yDRy>[yCbDs]_QpfE|^anᢷ訃TgQ7ed7lxM^vLnnOht˙f|[yZZQumW_zMlwDsgmWcCqn|cjZypN|TqmLun`cJy\hXxy]yXn`jiEPf2CY;kuvq_bG`]yeonIil?spDfxQZRyXvXs[i`rrugxYwRaDuRtXe|GrQvPopAgpC_oEcrOijzҢwuUm=Ee.AXGgwRlpA\g\gF]RfTd|הǔҪޓ~`mm[y]k{I\nOeaI~g:Wi8Uw%BF SR+o>xMBEUdRC1cS(Fx4OiEVk=FM_QDwP/RqA_\O\S/ZO0QF5IJ7fB8^V0YeB\[=jq>;NLMH\W3]=eNaSWBsPL_LQk;em;{8PTvu\`7y[<@6r<sp/loUKZCE*FE)KV,|d<>oryngy`cv][cO[`?>lL6L@MM*T^-Nl8Ai9:F0DG$>H&/E+1C./: 56;U(:N$>L-Fh-8e7WV0b+hKYO{zU~Uf~eDTAGM?O](BY.PK)pJ)ni4g{LlSdTliNBoN7Q?ED Qh/Wg7?E8e=7S&_\,kyHPoWEf:LX=RZ1WI4SL4YS6m[9lY;mR@KS:NA-Wb0Fa9003YAiz8tU|~glcLk?TxD}V<3OV#4\,SIU[(xWDu`u=Xo5Bn4Mm?Ez4CP*Hb&a]%MH6BI.5I2B?(jA)t}DFJH_S6nI?\D3JELK)e.cuXXTAZN:t9uЃN״H=/z='PnWY1|OBƫZvӑkx_]thaJK_q^ƢꬥnosqO^~Mh5|xJ2W<%RjBFY5KM9IF9@Z3O*Y-Rf2DtsFelC6O/cF](pq_rIExF@];?)d>P\cLqVUaz`wSiQd8foe6pj:SxTw`T_Д|ytلdٹtocxGsKxmeEEH9_O?uSDvvAQ^?s\9WgKeI.f7iPY=3ocW`AU]=K?,S`gS4QU9toAhL_hLkH^Y:}__iLP\7RE*YE%g\1je9?pFY[zBehbW]d6Uw1qrIٲ巛|mYjRiZ;gj@QhdYDoy7oVʎg\keuǚ҄^{J[a?=e:baCɣkꗷuSLыݝԀlajIq[9fj@jN~XxU}aQXdx[Ybª_js@^U:fi@]`4jwDa@~FzDwPhDUv5il._|=Gg3^a;qQenMwgAteJkφ´ۃThr3[j/OI?yO:`L;lTHqP~cbvWhΕ|golCVoDhLd?nX_iiAk[HTujVe}QnnOumOu[Ju[d\hzԥКͦ}be`6U]GmsGVW/?K,;I->M3RO4QeJ|]aLm~qNgY6a[*hi*QqGhcgKlIqIiPjTpklau\uC`?eL`FZf6Vi7U[.If7Jd-?N4SO<`kLbzgwoxDIc1Ic5Ie7GW/F]5TUU^T]Odfk֘Ȩݣӊ͜X{pJ]iFX^FN_BXj4Ql5FW8BV;U{FbH`}Mm~NWV-Mb)E_,@U/OP'jd$r;Vp\98Ww=D,OC"^X(kx3qLVS^qRR6jr7HtcCMIS:)eG&\F.\L9V[8KV?\U<}w6[_c}qZ[{]loru\xUzclW[XiFk3:iVjyk[T>nUCA2=Rd,:Y[?].C\-JS'_R.hX3ehQ~jKjMu|UMaW^bcPG9MU63iA>P=LS+Eb3UV1F[,0I45;#?C?X+/S-/;(A0BJ /S+PD,Ek-KO8MK&]k/[wHrb0QxgQ`XcZ8KU5VO1FP*HA0O: _Q(W\3e7tyDltUQuQH[I[`*Et9QV7eU-DLMb;ej8\uEQcGPY7x]4ru0RjHQU2WF6Sd.M_MIn7@d@EKBrW15xG?I6Ma+lm4Jctp\fMeQWCLf<8eA*J*c@!V<@hNDQ)Z`.lm=C{AC+Emo8e1J/FL ab);tPj:!@kuNzCU|:aF]N=PFG%Q_)N28N1HJ0_L<'-+x)[Q>ZC?A2VQ1T=>XO1M~7`)zbFgbRR_U\7i3mPɨD<+hL(YJ1k;bRYwfClkVÖ`vm`jrQmlĄucYjWÖ۷hZ{~6o`y]bUI}ESv=2iZ:1n@,acExgHP1OSnwq[]nMveJgfJa=qL-XF-o[7^^=ah;f}V_qG]AtXfoTS7FE,kS5n]/Og0^b2jI{ER<{͑q}^gizF9u4kjMѯҮӠ{_\pOW[SOWe8feA|NeKOx<>QNbVZǏ{؃▆kPhTgs\]ڂxMuDѫtTVJ`W쭕ɄiX{LMUeV[rɵqz^RLrqC{pQvn@GwI{FFGIfw+Nj,Ul/O`0Ej.vxH^a|At]ċﮚW|Cag0SH5kG9WI0rBFfIkJ~jYvppヤһvi~ĸg|t>Ys5c~BppI`8aEgH]|Ffo@ob]qs``tmKbWMUvZcmiPqbreIh[xf>IU:PUBcmAEV,:F 1124&86kUUdlzjpJGm);Q*BW/HW.Qk9\[oz؃c|ѦŠɔaqK`~@\jY~YJ[v>UcBOdRb\hTmD`cDStRSL#s`,]:BWB]RRK8F\<>`?AR1>L+X\&Yq:Xh=]l?oi=om>NpO7}[9O@HJ1RF-VL5_X9W`:nmFHjɰpryegtNQrmkh`YIwIBW9WX8vl/wOVb/ZK&7/E5?]DS.Tc)Ot4Ne;ch9mzDMcRFXNCF1F>)FG(-O1192=C MJ$Nj4JlNN(@C-:G&+:(5+?9;N"^U(B?:O;MD)^a-NKt5iHXeNB`=HP-7R)FH!;Y+0I.@><@'QR,_v8W}EaqMVqMQTXm7Io59o4UR=fx7ZdSaDCRN[;HU.YK/w[,IdH;R1NT,xj/i[o^hVK|XWYcP>C;XB,Q1$+>:k(`G=XBO<1`E3@`GQC'Uy.Pi;^e0jQS`ML^2Wn7ʊEQѵb|UXa:f5baTqUX~fyus:XL~|ȼ՛ӢuǒOtoc/uR=mS?_A.YU3mJ6nNAnwKiONl7NW3:a0BR4CE.j@)aM=K4l[WPWt>Z{QXI{Pab6Tg2t6\[vN}FgEI^|5d>sTkWlm[_fF4UH9OE7XC6vN;RFѭxn͏Qe8VR'@U(2F*Wo;VrjBtZnUZy8HG)M1%GjMb[ntIboHjnIVMlmyjY^=Z]4byY>d5[S0n^6ekF_j~XI].}gDo[tMsoJFj6dY8owG}JRu=dIek9\a5e;shng={^jʅMe:uZܡcVuyJurPImPfnIUu5heB^Vq>YN-zJԪՇ߼f{duVygzkfxVe|IgNX{QiӺz^xZxLkDrEay綏ʂj`uߥő]MsHnI_τfcz4}x=wCiBIz38P%Di+<@&:A/Lk@`VqvsdvrÕ~zfzXed`Hh}SrVrHV];UF9fratS_w=IYHWsGNL1X,~:YWnzZUsN?oL;^?9M(3H(E=">H#XF(}V*FFuYVut:Hg9Uo,;=*3(56 9;"=A&HJ-Ld,m`7n7pK\TJo^\PaiAWlE_cJp{Jn[^wnɶuzinnz[JustrjUa^rpKaYAerRmwAPP:aM(G1&=%G>!Ek#Cc?T_4Iu5Yj4r4qPJ/rw(B=+-G2Hb"=zEDU=YQ*Wg5@iD7?8=?#JT,IX/7Q2;=(6D);?%;0(4#$ 5*HIV_2NvIHY7Wb6bs5^QGwF5SLA6#8Q&4E*:E#BN%U,CK+FU-`e9S~ARzMZtWZwKM}MHrDYd,hn+V}H~mIbRzXye[`YwEhzLek@DcCIS9S`7gv>^M`Z`P]ZuZtRaMyIY[{RKYu_zVcPagKnLUgdMF]4T7=L^R LW4OY3Ec7mb*MnpLSUqC@j>4T/;KeL!d7\lBSH5__/{9J~GTcNSz==a2HJ3eD0*a1`.9?0R/#Ol;T]JP)7E*D7mY?o[0XMQmB?Y6N;&8>$ '=&GD#r:$fBEp{EDq@p5a\n~HhDf~Ddq;Mx8Qw5\j1pn5~a8ZrewSwVf}_gf\{bldYŀdk]|OfNEg=n{ImEfG/cBymLTTQp8|]ēkjlfOs@uߙ~LG}W[hItgDvUMK1ye?vkJbbi?V_3alEkZmcJSqGFEwzO~쵐ʌҚ[yTa>DX69T-\eChY0Zr5]ˑj}^yЊp]hvZ]:`AvyAyrK|Ubz<_~=YeŀZvrɖewcUa}X{UJ[av:co?bRЀt߅jbW\ӰxgV^sC\ƪ~}el_Zf[ٳx|Gj`;RW3|cPݡpZRDvX_OjD4u*Jj/K}9iNzmyP\t6la{ۖGih@_lCsjNnqCtLtQrK~WA\ZxNycC{bƀo͌lpNeSm]mUjC`R]yEkyDoJlnYiqd̜}ڌi\^KZL6W`?Z_Mk}p^pWde>:F-:@1CY3GZ;KVDO_QmqAXb.HZ1L[4Ic6Ja7FV1GQ<*0)$5 *3%::RMLj+PtBzY=\/vwBZg_}efM{jSvUquarh{{rbadzYLvDb`m`Zr@rTgu[dbRZnUSsB;s=2P-1H7I"UU*Um.HAQa-l1I}5@[SOR6Ib0A];GO:F\6GoDBmOCY;Q"Z.RD"FX)6J3E8'SN.[g=YlDZpRXtQ\V@GBX7QR+aY.rd?xLsXsR`sYdc;Wc;VX4Hb=JU>NM7_jArGabfZibqZ``Mhz>^JYsLos?ssQY_ԅ^mgvyAx]mkmPJb:dH7K*K\"I_96`5:K+``'xo:jhJ]{>USUw@@V3{IFR+:m/2H&0:FBBU#ea,X4QeGOp0im@Xn4Nr5EP%?<)LB4v9tpRZL?>E1XB.SW:{`6ii^xL|l;M~a\j7mGNXBk0X>$S2rqgnmEEn5Q>SSO]B?W1i:WUuid4LWTtA[W?]O:dj8[O[Ij0fq<;d1lATT^Z+aNdl?|0dTKNv7fj8hoAY>+|WErZc4(cGp}SK6a:2IC2t4&{~jqmcdԂo[ab6Q[-_JlmkkP\t@Ý|ϓyp\avLX`9]]IQ\8RoJXxF_[4yc4|lQucHBf9Qj=cqC_f@otNm]QtBtSİxc|SgyR_hN==Y,gTWMlEhenZWo>ޡkxxSo{Qk~@bGNaeZ~[nGrnHi|GnFZOto_ȋaPP}JޒWVOu`ň{l񨅶z`VTKW}}T|\ĩa[\}]mSwqQq`|sKtwDkk>WBW>|vS{Qi@yYoT`;UP.R{1Ys>_~PS_X._a<~܅zGerBa^B\MxIxZ`yQlP_QxVkQsXlRxxQb^paqGnU}\iAex>vDsJnzDiKZ߸∬ۋssjkkR^_A_kHluКbzlPqk[zwZjlKgmLus\rfq{LMS,>Y0Hc:GuC\O]BDf5G\FWp;WxK[[iewPYJn@b}HfTj`s_~[nj]z]zUr͞ԛ˞ԃHST6=b6Kt3FM'-+*BR*:M,5L(6E*M?%@J"BT2w>/s)@l;S5<7A6%,G&,A12C#SEh`!JA]jWcX4^0shI[s\ktC{?LXx~r\}_IuR9`@8P.DMFS FX*cR.MA:VH:<4G=#Ii-R4ۇ@7-b-1'K'cPQKV|ZLNRfChh@pwIs]StEjabj=d{BQzQcLa|??SU5T5d8@Y(@'C2#_],UU^}SmzQ{TknssjbUaOnGKf,^P0w8dhhX`gevgww9x_YXpwLFtIB`9:W-_`)RY.iN:F#JS@M)5B-`I^r6cJvq:q`bvXf3y[1=2VL(YBEpCD\,Zll,MBKPc1La)ngDX^yc@DSN4${U-v{dIMeS4}rgG{Ra|S`e~PvwoaowJrtBTp7nDcJVe9io>]x9l|Mh|FvKqyZyzJ^`7aaIɅwYYx6i{>fx@D\0Sl5_YYpq@HS*HS-biF{nrMiFtYBqfB{OUrplnnbyd`Yhc;[_KXk>Md?etbq`Tw;iH[qWyRhXˠrёsopTo|IaaA]gPߛiRzpƱ}op_{R}nYf|S^kCXwO_Q_Zk]ukkSQvA[yI\rFbeqhn_z?Se4_{CfKnVqVj^vqk_bԶ}arK[W[HS_9;I%/;$3>-@I>aeKczGg}֦⏜iyk{kiSWuRkSyEdp<]`9T`@_iGklEf[6`d;VsBUeF^dGl`A\bGRK-WY-^n=OqOMiPC[N=VE6P75J.9F*BG'_?;AL<$8H)/?21=(=O&hL [l(DNU_C]Z0]+up0cTq[NTS(e>tv5a[JaQiK^\6tcBymKZ}_CqhLgHdwAp~AqRtXkeuu~i|mnnsv}iu^yd\XE@MHh@Dp/:p88W1MoCRhFCv??[27V.:E(MH(bX4TKtv9CkDi3bW4n}R>ZA\7^eVeW[i7[pBS|H[I@d?E\(2O,JA!>D0/Q69?!0[%5E$<<[:!gK$GZ8^f7~z5Vm̞`nN]?S-o]=hy:OpKSZ9h:jIdzB{3gtxT{tu_|Yj\vwn@QqFys8TnqJOpGQg-Y~9lw7_zCh^2Mf?NJ)=9/`J1enD:iHL@(Z2bE0nd2_DYb=lIlpZzdDpSp{TcMwoI]Pnt=dEd~o`gxCmQ3``B^~RQS6`\1gsSW6'cK1nEaUSgsQZ`V?gfC/zkvi\U^i=sP͠_ᴜ̫eziEt+*$]7'T8[kQPlT]DzuSwnJsWnh>u[uiy~TVi^G5[P:PN3brFsyIkYtalYR}CXsENg9mo:~^drNccWC_6VZ.~R…Oe]Seŧ[SrvP4ri=Yk@WS0BS+VP:\pKYj:cGRW.Ld/Kc,Jh/fj0_l;\vJG[,Mp.ezmmOk[wM\֟wpFpv9MU^Ո|Quȇ߯irEs~RQ`yzwÄȀm|[\pʉWb~0Yk*d{7IwM_ɛؒqTLJm2cv:Qd?gy=|L[D]X3D>-hn9\uFtprmGtuZ]|Ij]pԝݣȅkoKpfEsawFf|H|UpYjqEjHrM][}Sъթ菮x|eiWzmpy“ekOsYƂyYwcz`hef]^W`Y{buuwefRgced\prZxPwp3Ni7MqDhKrLfYz}ՕʄʚY~Pjު܇bnUjSu|XyjObsJ`tZ?JC3>M.5Q?657?5(GP,VB/;_15>8n0/]+Bc7g9"=N-AX*]S$=t.>`G?P/KG&[N%b[*^W9OU:NK:\>6U1;P'@W0CV,EV(>V/M[-Gua)Gd+[M:dW9l]@dcDZ<_<+so?N[KE1tB-eb?hGneRl~OONM_>\f5XA.O4dmOWk`5w>f}RMe6pB(Os@G)\C(~yAă~ʇ[iDjlbiA]GIB,[4~H-~oAhHWhdv9b>v_o]RlcuFДܰaw9b<|=1ajFU4Ƽѣei~΄iLZt1bQ2N/!iLCE/Q1[kK\Pfb9{c1}xCkFmMQ~@or;do;kzIKe8hccbQorHn{Fbs>\xI`LOk1ZC#Zl2wrLq{IWh7E_:j^5^`5zzNwogFa)Uf0R{CEZ0KpMMnAnUocMBMf6Kx!9M)KY(F[$>a49O29F.;L)QH%VQ'bV:I[;R^7]Z8u9~Uh{r_qW`_crOhGkyCnP\V^[Xr_CfLjO::_1.DK.>#?2!IM V_)Ph.0gC99.D8ODUT&J]8EY:YjAazD^RaWkRc`c]U[JvUOg=YkICM8FR.AQ2?a3>S/5E(T?"Pq3TbQYt7V^GebBlzXqfcbWV[XPVU`^tV[Q_bPwQ>mpQcZE`HWP4QZ8eL:cFe9QB/Pe=@b6AC+Kd1_1XO:YHHB*Sf3GxE?Z7EZ.6F*$195*A 42#EFDl.<>\A&Vr=Z>7{K6?rNLK<]=ArܮlN|xMRFZ4DU;^H3i@htbbNZ_3r:(MU;gh2aKZo>nAn}oHoO~?+RYuHSA(v\Zf=n{^TEpA]UUY5Lf8OMP\1Fi3zLs_ZNj:LFt^S[eRshrXXyEHyDJc6Io8oSi4gxM`?Pi0Rr6QW6X`y‚vgaW`}];Wt@uMPd0Fk)mzGux>jN8cZ?pWisCfQxto_k˚lStXVKuFvTrFsmGgKzMopG`}R[_ty{˒cktHTi:Pg8M_@_u>Wf=^p@LnKbtDYn6UlBftCnw6PkZh4Xa:NeNoyIHCGBMA9N0KM6j}O?JHX0Vb@;S?7D:7F.9I0CV/Jk(Ak2Jb8Aa:El=Vf9a[.OW7B[@LY6_e2y;pOjmu\fWXgRS`APm9[eCUm@elEUHJy[B`=XS<:O(7B:1J),4(>2TH Vf,,zD,>:2< 5@9> DS,NX8[a8fiB\lG^nISn@PrFViCNpFLlA]sBt>_zHavI`|?eN^QPJ?qC5]97Q$N[+S8ExKDQ->c/Ae8He0cf-?SN7@+KbBTIKG@5U,9C)EN!Db'I`+?Z,Ca(W\ Vm.cb/iq;qGyeHXV/TG.>a.3A0=:X8$lzA~rOcCNI9nL.\yIia{{JsHyQf[rS9GqS_i>lNe[I-TQ(B*pM4Nph>Ub3i}EpAGj6Wp1tMwKqkƈsmgAlT2pSȼȟأw|YhvBnyLvdtW\siOȝꏗ_cg`Ȥ~Å}HfuVã{ˠen}~Yu`{sR||K_k;zIsg7zFZ_zAYyRTnMS_DLP2`1DY4Td*y|:VEArMQ^-er/G]F{eUg5Lq2VsX_L=MD?H:FD,LP)J]/iR,~eIrLZp{xbi{MBeOda8G?Ka9NW@]s6iwDH\6zj,n\NeBnLTf4Pf8M?rYk?ndF`tC]xmOXJa.YL(cL,h^?lcIu^>bP[Bu7{LgxMbn8ug;gvNnOofGl`H\A4g3(d]AZ=3_|z\[^b7fwOfa=c]d{QdVYl6q|I|?w\bUf|ךN}XTF1:E-Ka3OH-;I)RS'igR\kOLslJT:sS_VWCl]ir7\;`J;dl5Um>yb:wZ9RG9za[D8lMB|oS@0J2,vUDҡy˪{OXᦴǏ̏֕yccB\veCsG|_?Gh.`C)QnmkuJfc7OP6hK^FmcVuZBm}9Rj7XvByNm|uvGИҀcdCeL~^RС໱߶֏dhٸ]_p_dFxȉבxjy<[ƥ}bhDXUvMvI\K}np]^jrI[`DrZjhUoDoGd{@VPvA[{NWk3RqYhf|eŇŀvcMks5hz]ϛ牒`dQsHwMjKcTjdzYgLxbuGX^/`J-`KYtPYKmMvFvXn|{RvBrsFkK{^M_4Ij5Zl5^w3Ci@_iVfVldrjfCC^5SkPvMco;Sh[oNv\TmwNp{Qiw=Ob@WtDPk=DiF[s@Vg4>U9BpSZLWdGFLEPG:Qc4ZY;CO3FS:CD4>>*P[1_`AplHA|^`G\uX:ZY=P?FtukgdXnNApF]cIm{@nRcWXXZsHLoALhJDe9UU2HT/ZU6aT.Gg2EU<^X,f^6`q@QqGPV>UA4TH-S_5=R1gU5j/`^KfYEjBDl=M]@9s11G29F(ElAVvG_bCXY=Rd>8jIC15B2#:F+@G.*Z,/G6fkoZ~Q{QBxZCpXCdIaz]`ycXX~R_|P^GGX-r3oVep]y[XADv:1YB870A$.2$G8 N[)cq:`hAhi:J`8>W/ZI'eZ1y|>SN=B7u`6aR4og?~xSev5m^]ҫ҂vhhWsPgd{^egi`|XytayNVd5NR>fjT`zVegrfOCHS7hS;bqYrYi]DJy>WtTqv}oveysvYcmNZwDRsFhMvazyhWuImi7_cJuU|CfEW{5Mv]τc~E]rFYXD[LsWYM[u-@_)Go3Wp;\VFsm\{]q[ZktAXM3Xi0A\:LeEq~>Uk2Qj:bq;MkHW[lJdgC`UDogVpqLamOnwTXXX]_YZSIb]flmEnHIMGYVD`xA\mCRd>?P8BQ5mU/{Cl\]yb:ibGFOXF2PH)r<)f>w|nUZOdNW_Mfg?i}IlTV}OJuK]S4[Z4JbBQiAVl19b3IR8UH'EU(KW1fX,Oh=WcIMb3DW4F:,MH-:X5@D8jK)jt6GkJZSERb8IkHedDC@KhL~{J|m;oRHHW@^X:5s>17H*0';3#RJ*Cf4:fJDR1;O)5H$FMOV'Ka/Ia]TFL+LL&;W7K[1?f*IV4EJ/:R8NR2ce,XGUvNhr>r9`m:AV@F_/7o=AN4^L-~e-Cek6BK=BQ&=M-8R*1A.9@$ZV+Uo>8[4A=(FY'K]@DVAcCd\>niEd9YjhpmwdvLQaq EQ]e2\u:cxJlr<]FIf;aH/Mx@jr4l\vMxzTGyRLe:EP-W?!me2YV>[2kjFI0Q1NqYc:|l9Ql[z]E8~avRHwDA-a5*K3UN6Y=%`f+aeEewAAsQ`RbPm}iocSbhMkmQgYb`fTmDE`AO6wlb[zH]OQ|=TQ*vGq|Ffi~reEcbInR_bVz?Po?PY(TiGܙwUT]~el_zdxXyS]V}|9ky?`_TQsoO^NrO{^]9qfKgT|okM}p`{baYyH9sPz}lv`rvSga]vKQyuJLds_YwUn}UzdAn_GiFsb=dW|xXlW\7gl:huxaXZgn?\^G`qwsXZCHV4YK6f\Gq~KszK~NjTN~K^pRzpf|մĨٻbNri3;?!2?7muEgv=ZvEhPhE^~MlQMj5;I4KL4^Q3MP1TY/Ye:Qe>f@[3Ja=n[`\x}AdfDaYpVi`GnP}\gN^p2Tp4ZyC}jSwk]{F]`>oP0_R2Sf-HXl{KVwTMwP>aE0M9W6,pK&q[1vbDolQfnWe]W:HG110I5"Eu5Fg>;S.[8,uD#OQ5j\>Xi3BqI'NX-O`DPW>|nz>0QJ2G%I>MM"A]-CQ&F]-g`;t5M_@UW=J0C7%GP#Ol4>^@8O8JW-Rd2_{9hDlt>PLP_J]3;BJgpO]2Ci86`A4D,KK)n[/d=NUQW3pc-rNmaf^f`~?foIh8n4BO+Pm7Y@gBiKyTed~McpgvF{?TXPOV4Qd5E])AT*K[-]/Le8W,E^F@+ti5NuYlh5r|IKz[VJgA4l5wx1xCfsq}C|`txځm}U[nVGaQe>un/bJ[{38R'ka5Pj4UY6cpH^If=Rx=J^+HZ-ezMhmyVyΌxkgkqWJЊuWmeEOLq{Hulra9nYv݈zofI?Xr8O]8_}NbFekUro>^t7`^1|uIf`{Cgz^pjدZlDxPKOZrVtVeCtuBnrNMrg9Zg6Zg8rHXogXǰzءfYyHwjLm]n­ٖۜ׎όϗȃehgulHj[7zYPUx}FS~`pZ~fhkdgSmQTw>Eb[bgRAnHv`|\cayzoz|Y|vǙyjtj⨿uS|Dbd7BI/@J8StMZtFUyB_LgJYv@ZkFjvOcLd^BdX6\U;oYBabCpuRnvD_;^;DfA`}Omp8Yd7LM;VZRxgO`Lv^Bzl]lT{9Pn6SnQghnCYq9IUbl8Z^1N\5YiGmlMlµ_E_sCbnDef@Zi;UW0FL5KPBjcRiuWdxZmfphfj8cyHTiQGdLHaC@];1E;9I22C-ME*hX<;nH6;M1/#!E&Lw0MrhBVNGQ4HM0FO1Ua9Nd81P88:)EBFY%EH3hU3R.hc;FrK?f>A].I\2EZ5:_FPS8?W-FN6RM-^^1Zj?{KhMSz[@YE7C8]9&Jl&{lOdK8\K2H17D-CA&UO+k])YLPih]CLP.4\3:C*DK ;d.cM8gn/Id.c?*F))145[5MP'Ee:>h:CQ%@H RL/N]CJU?RZ7P{?eX?Xo6@fK^R8\l/XK.gQ0eP]`_ccZf_fLcuOaMpTlTqiCNADoIHX'YK BkG@m5NJ'9O,g[Y~os|x~keZqO^|QcQQgER^ReRYwYj]u\xewV]BXrFenA]Z4T\9\_?]_?h`?vpS}Bb?`}LhU{@Q`2QF4XKN}uYkmKvdMnxY[wGhe4N\6_~bpVjn;UkA^{LkPaGOm2B[6GX>\pGlzC\wIktD]e:^Yddgc\QQg:Zb=T\8FO5HN@biJgpIioRq`V]O*@m4>pGXR@kR:UX?8`@6Y'ZE8-<:3#,E*59*S>%VS%LP1FQ+FN)BI,UQ.Y`44V2.7387XH#Qk/Cc<9RCX0KN.OR*Wc=~ArQPnZBeF9K69@.X>uf+_cEhoEIF:W/&Ue.XaMU;OY/Ol.nuAaFYtRO`F\7WNQpfbo0d9eM\N;pSEB1:O) =CTTUY*g4>T0h^6SoO;]>]_2x>A~tS}Qeċߢʜod\`Xpm1fIJX+\C2E6=H,CO#^_(j/Y[{YGyN?Q86B#hOmI%TK:`Y8kckBNycpahqHhgZLFe,$FF1C2#N8 tM-l1VCjzVka]oXj{Ac_:hd7wZ0ȣLɽ{\cXrTUTq^5nƉ`SmgGi@znalbALP4tC&XV;g%oKČx̤]dw_PO=b[swf}qLQJgKmvj3oydstS]z:WU1q&jjDڊ@Z5;M(^QZsB[L3|sDWy=xJjIca=[cn|Gqr>r{7i6lx:_AEc.nmA`7Kk/abGmawFsuAvo9HI/jQac_`kvJ}r2w:um:sT0qcZa4QT4LICnqonuO^~BWuEUhM]nSsCCj8UuH}SsD~<^t9anC`mKxQkyJYtHIoSbg~fwhRtCRRwfuϒ݁pOorFkb@J_XUDVM+aa6DB2`K58=I1'GJ+IW;AW::J6/32-)(J7MP#@D/B)&E,<@$HO*N@"=I*BV+@O/>B8>F/@=!EB#\`+jk3Pn<3Q@4H-@B'\R'co+?x2SM=TX/GoTMcHDR1AM4VQ5pk6_jDFODCG16O0>?-:O#cP)u=`d6iCP6GM'9g?6A9-B",9*+00263H<\g(ik6AK`XCti,`cYrLUF*bY/^_4PrDCh:Ib4EI(TB*OP,]_3Xo9D|CKW*KL0Kd=QL9~Y.TV\[Jhi=nsPKYYk8Rarp=RgF[m6J|FBf9HO#Nb(Tn.Pj1RU;VU6WB[{?NvCa-gw>JpE`+bK2zj1`IK{Hq_0~q2VŰnUDVAuv1yZO|N\W.UV,3rP.@%:@nR&X/_eGcO0W>.bB/Zf1\O0iF1sB+{VxmpsP|iYVa`i`agn}TuFQSiA-xDAS?7]K,MQ/XS>WaDdP;wtf{JzFzFgn^qD>E'I@BB#?$Ԉm㐯ˌÇqerWe||jj[jʟydzet}xO^PZm/Qїu͜]AnV8yRBqTcWqa~Xzdp\vs8lz[ɒu]7V;]o8^iy`ɔjϜdZm[Y~LNP?NX5d[BrTyi~݁^]bO9qt5|PcdkT9R`3FZ:[bN|q_qtIbGk_=zb=umBhz]nVdsU_xc_`ap׋ogyKmD{P|mKp[Gr]Fs_u|rlqe_sSng[cPK8Yc1WP>tvC^@f<`CZBGh9MoHXGNr>XsMdjy|ŔȐĕk^?WK@yc՚೴㣻◔wt]qo{|yswj\Z=JmBQgHlsQlvJn|Ig}A[sFqnjG\k7TX7YbIrɧ|zKfFW{H~}QLRuMWz7J`3QS2dX-XN2YU:YeXogxko~}rzxyj{idfijɏ׉\lm>VR;Q];W]5RM5OR5LW38K'/B2BL9_K;G`@&oF<7=@E+6I,NG-$K5+1:7:"3$ $445?GTI^I!BO.CP46B8?B-9<(V<#Mg+^\8B`8:T7@O+NT,Qh:Pi4<^-OL2]U-I{SC`LNP.S^4em=[eL7TAA5,>C'=Q-DJ.[^,{z8[an8YIDN5EU'H\1CV29O8XO4CT1?@5F(t7amxijxZIkYvns{F^zHQgBnn>qt:wQUXRZ{WJi:AO95M.CF+B5;Q$AO(,I,)1$&2.+64W6Zf+WtFCQiU9KBEcJVJ+^g3YBF}OC^E;N2KE(LP)Ja73V6CR0IR#HD-RZ%RV3E@g=Wc.s1P:IWGJU+oP5L_`_][|DSBDlB7n>tH*Uo7;XILG&]Z!nb>\j;EP0Z;&qE(^rvAmaV`eu78{R:4%C!xRWUE~^=kuynؖHutuct}[ftYIhmKjS;Q,/S-);\:TebT_9IuMmh8dg3~Tk^~ƂsmTfvX^u;fk,`†}{dyXxjwTRY~dxpizOYbutg~:aa>b?|`nX{o™rh|~^}`uUtNvU|gzh΅qفwāmeib_.f@j]Jf_FhYIxOevJadDQvCzY֑tpR]cDsX|X_UV`9tE?kZEfacZzXzSdBP`:iW:Nuh;vF\ei9KyTo_9]=*cP3[P,yInS}etcozEhGyy~Y}[x⣋ԋɃysXoNl{XkKk}4hi2zsAv=T*[3g:nlQ{ecx>oA`WUj[]U;`hHk~zQisGfp@Vf9m];ti`T́y@hQ5rW:xQ9im=svC|uKcAxY@xk?l]:ьhefjlfg]c{HQQ*<7*Y;3a?9jRA}xK`zEeX>gK5}vZ}z~`jNHc9aqSSy]|f~bV}MmYjd\hcukhTIId>Z[@hrNgXzܩykUpYInVAcQFh՚ГrcxVkb}u}ojpa~TfL_UZnNioZ}Kcc5ak@TqDdwRuiQ~bHg\6PZ<(X>!J\(AX<@P3FO*IP)Vf4GtCJf>Ca'LP1VO.:pQA]JTQ$mg-awI8iOHP#_R-[+m6tEaR:}U4JF3L#,D*)?%/8"B.8M&2M,+5&(3&..!%3//R:b['[}LWQ^r@5?=TDUN1za6gC:Q2R{vMVpMH_EV]7IR6Gf5O]+bl39f$]P*nxHuO_QMOcm;x~EsoVbg\qPhw=DDPR(a^&m|IMwJgE&S6]O4V,jÖij_sRFmBk9\AnW}~XUQ?fM`/;j98I'E<&~A%CsLnQ5rZRpN[p6~=j{IwMpZ[~wNmDy_RPPm'k8\̍i\}fUjMqGqV{\`ChRCbDQɜlڭŴv{fgapR]neK|D}xJmduPxPbQwG-nEesGkPhxe`fAtrOovLvb>tL_El^iwPnkNr}JccHaX=UeEuzSg|LX|]zlGjl7l[7xE[v6xI{O^b@}R5{]Ae];pmDcTmcnZas8Ts.~^ohja;{ӑŌvqnKa_={r?VrT_=\`8llDEa8[M3mo=dCvYnb;`tCcyF|YTusE~mQsdO{vJ{^E~glNvPhxGUV+H;)QE.]N9u_EshCsM`{]evhIYB4`B6xXBdtJ[mHQLfRujn\|cYt|Db|Dco=YbHnx[pXZp;`V7q\Lq_ڝڅ}IpHB~ODvU:ET*1K+YSDiRxOn]0MA=oT[jNzw\ten`qQovC_LpgshzivRcbFbqC^uEmvI{pJ}qJZk>W_ZqjThh/Qb*Su<^r~ؚts@i[6eV1\M(=J-GV5?T8Ghbj}VdPdYhbpn{v}Ӷآtsw]b{S^mRqCRqUY\sGR\3On7_WNA=V75U:%@3(-'--2#;#'5$<5>M"@O+FQ.OK7OT1[@+VC0HW8KhMPH6JR0XT,ae,Rs9:r>Oa=ozH~F`ZiEpWNU>hY3T<@|XBQ5@L6mV3QWslT<}|GZrSg~OEjivvSvy@rJgMKLgZ=giHj&@cV\!_b*=pSAR*T:#}T-vvH_kSDVo0?n+4[(KM"IK)_J#Gf6SV+Ss7R^)Z`8wvD{m@yYVSQb\3xUiuw_YE}qCqzotU^]OQ33c(b<?I'C*UW5Kn@AzXbdfOs{IbxpԄɔxdӏMNQp:`i)`D]ɝkڟu{KYceqZzhCxO|`hy;nN_gVZYZxȡxiۿ~ΕΩҹpwevUQ|ŘdXki8|qWrqsvq}Gdl;`u>UOPe=ll_r[UXeEY>upSjT:yM]o:Sxbޚѐ_m@qK0\J0oS6SW1>U6@Z@Nl`iqtni\dTd^kftouzi~p`lwQkqJm{O^tNGlT`yWaNJq:9M6NH3LG0RO6AY8H]?ZT@Bh>.bE#>3)"M,?B(BJ/FY9T[:\T/VN4XMCDIAXF8UO/T?-fL2LY;YJ3IO&RB)RF.hcDOqN:F@K@3VN+Q_?8PJ"Rk*Wt3Vn1]p;vp4d8Mt:Ha/IK-cP$L;4N?\Io=ER:Mf;[gC\{SRdM>]6AL8}R-DZIV7Db*>d2CT*?Z3Lb2Dd5OV8Wh@eoAoL]Q[qNzIm[_ySTvNEd:Dj%?`(PZ*a]1QtMCc;Xg4LL5n_,jIqJh`iNI}Y;]BQ*Za>[30U)YF/gJ/oKpRk?TsozߖufVa@fwtW~a|nWLL|SewEVwO|@wTVa4Um5V("h>&`J3ZaDDmIOHHT<2\G1RM?QGDJH5PQ8GI7_I.TR5qU?Dh=B>3;B(;E+4U$=G'?S&L'T[1VT-]V1Zt9DzHGa+SO!JN([Q,Z}DZ`Gzr8KVbi3=MjEKInFMr8Nu5Me;\k5btrlIfS{Ga]FpVnWHtGqo2KFic.SyuxNw~[suϵTŸBtSGp\˙ft9POEJ-3F+IL-_m0xGAЃσux]}uk^ZOtYmwLHgrM\NLulZpH]Q+/F%)+55AI(9W*Hc4pCIEwHSCHf8fsEԂ[ߍvԀfBNx;yQtlCja1cei?xId\~y}ޜ~x[[s@EG,UB(zPu^KKN-qsIXMrw:bV2_=Yxd9[d1\~B{Oyef^|Yp{˩~bjNuOgFiu6qt8ZiVCb[kp[}gyMlMeJrCg|O[gCY]A[`BfzX~kxnnbZ}OtDqpHxa{OUs?^}EG^0P\AKq5T`+[q1X^4Uc?\n6Up;U}>Lg.CM1EW@FbMPx]TtHUdP{pičŽգ챳ywVch1=F!.0/1'GIJ}rvwvioD6H(D]-Rq/;N$BF(M>7kaN{vKzzXZKtsDz{VsOn?ofIsJwFqJiK^~\uNNmTmQmx@e^:aZ/WS'F[4`sBRzRcWpSoe{ٓhp?Vf5QX-7=*JA3WZ>[wG\{Nd|YpNatEXhEVnU~hcWZxMQv^y~TJZITLBFQ?9D;O<6QJ0SZ:-O=77;J>+U8$h=,gS0PsG>aUGA>R91LB6N>:F@@M>3N@3SI3WJ0zQ7X@TP=fKWX1Pa,EA8VF1Wv6LX@bSAQ:NW35V58<*_F i`.=ZPSOWv'No*K]2>R,-E#9:3M#58!>;#IE"8O3RP3\+CWwRzVgAie6]@EnMLD3ZB$CA$J:(dT/s{>lsH>xC5Y;@K2JW(UF1AK6PU7[_3of6`Fp]NdbtpKmnWcQbKa|I]IQ}H^Mv_Zw]~TpPQUO|O9|J6g:HT1LZ0Ac/FT%SR(JX-VK.Cx:>O8TS"De*Jh&ui.bSjTIa`zCmDswAnIeW\g|M_FW-\p4zESr~zUiaacM`5[d6SRVXl6tZ;hLE:-_68sz{fFrfkFlC]v[yuH|FȺ|egwag~w}]IjFpNX~yzci]nvGsL焏܏ׄqeE_T,ac>j?}Vߵݹˣ{vmPxKcSVeEXzJmt9]fKx:prɂbwY|Jg|noUXnmtbprR0qS([\.wmʀdB\9PGRI`mg>aw`xFwAUe|{O~Fd|Qj\xCov2hq6fjVoL^uLvWa\XhT}Ɗ\fm[thBaIvISvMfn>RmBA(MK,I:+`T9n}Mi|e`J`pAamJhJaoAF@DI8RJ+oM3o];DPJQV>G3PC=HR5HM=KK;LG:FK;SL9\S6md8`?\KLlUMiAFj=B[3BP4?V)9^/CS(JP"HZ+He8nV5\(Ef\UizxWVWxO}mCtA?jDMc?jcCq}KDaC]RIP7JR;D`?K\?E\->C)iG/o-K^Frs]a8Fk>0C@@? QHgX,TxFQ|NBl00H%20445=:K 0M+;1$8:#<6<@!AV*US(W0}L4Mf5Be@JS0IM&FE(EG,9P0hH4pp:O[F_g@_j;mp5nHoMSiEJ[0dY.qx8xZvom{[id|KTKVfHSX8Rd?R}L>k@;T'CT%U\$BW/IH,Ok+9f@Db-Dwd?YY|Z_\UZRf?PxjljIZlK7kIN~`eJŠL`cPL3J~_SoG;]_RdDSigLX\<[h>T^4^B^aǺz|cy\Yt^]Eer3nq:mS0jN(UM&[2cL_~A\uvP]XR2gR0vNKuHl4O]~ǦfazevXVmdTZn5uS_vr\g@T)rHw<@u@qNꊭWREV\7qAX9aNRu/_z3mOvXgw[`rAkAђaR*fT*d8cBbCvJfa؅ޗ_``P.\WFj}?_LkqFYvXca{}Pxc:iS9mOTHaOhqGxRT`\rMPAlt;dElZ}RZrQSXJg4DC*iadbWiR{HS7_w;avmLN[Dv~KPe㴊oEi{?sd{t}QYf'fu%LhQzOitwrhuyњūhdE_DlBijEdK|hNzzBtLqOnpInGnSjuZuz\XOQny[wDuQeqbob}k]bxMtHmsJSmuR|m~LmZmlKRT7QaGxu§ט~]Zq6J]5TK/II3^^J[nZ[bH]WEfVFu_Mmarb_tWuZYmJY\QmlObeMGD)-/3+<<;sezt=Xj4Y{Jk`t^dH@\0@G4^iDqC_YZ~JwGdEWj7PR)QQ-VX=r\e\oOfBXk@lk@gq>Vh1MU+ED8DU8Sa`dqImwIeavƏkDGU$5D(3I/7Y@L^A?Z;Wj6Qc5Rc2DV-AU$+J%0F7LsJIOMuSXu\wUbOB_YFThMTjNPaLJ^KZTEG[FXG?^L6dW9tmE_gHA]KcPGZd:RmBUU?TC:\H9XLM,BW%FE(@H)\Q.ZZ,wZ<^.yEbjjo\t\deEdiq75[.2J(1G"0C.CJ0J[)2k985/*81=3?7?!4:$FK%C"v2MЁM~g`B^g=A|M/[C4?.;7(X:%Ln=>\BIP*RR28a>A?*hC#pm:@nKS]i;7J!pz`]hlMk]:qNxob[lcDa}dsnTvo`PaG`T=f{GRxVRKxB[xGZrNO@vN⩀mvg~RAm76)L80[(51795?!aN)e~:tFdwGcaQmi5zL.~F>a:rg6rYᏎzhpShb5yLf–l\nSڜczcZDwfrNq^Q\iP_]ʕYTA^kJJa0ob6͐ks_uࣆʋh[_jLUھiG})j<<1KC$UZ$Tq2bv:tZ2iG|Y`b\@RCt<`i5{wHuUrkD]g@Xk>Ƅqy]vhdpYlTrՃɇbC^6?C#^U-Vx5pifyMQVEULyThdowBSt9ohEyhyƋӇzhȋZYISIqX>YLfC]u?vSeGzMzLk9n?zmDachMWwAx\hVp6Qm>Zq"]~)wFs|MujHiłr\|N{c[{QPvjDwhO`U{A}sQu~IyOFvzRtlG``f˯܃KzhBsj;EdhNri8toAtSpTcLb`vclUwc[}NWZmj٦UhIqS{N_X8SL>vfNǟݤԅo`a=@T-?K(=F5S\?U_GE56E89]GVsRwpayy?Xw>eUlMi~JlNv@ny6[h/HhFgyDl[3X[.QRBrvOrJaq;bb4OK.`JM@@=@.A3,V@+aa0Qy=DzEEjJ;\99M.><(gF)Ag9RP>^I.mK.wG.k_0Qk:Hf=EQ0Q=)HA%IB2TP6Yd5Jh=NV=[_:K_?4N6,D+'6-#:;P22K29A%eU#^>^_Gbh7H^APE5@[+YG2kD jzKeK[VN?@v_a{KZqD?]CAU&FZ)3L4FO&G[-EV5^z7ylJdOy]HmJ9y@L5[C9Q0rBk_02IFQ(L[#ic.4G-D'G?OL"Db9OS.Lq17j;b\hu{uEpid;9iFcZ>TfAdiIXȴBbIfx4dp?BV#^`-hq:hBfzHcScdoy΍ЌÀqchTqLJi@`7if>^qEHrIIhp>Wn6Ob(`f2Qk4lqCZ],R~0\mDY@[aX}a{^`xY||FkvAvgIqnBYP:gXF{zIaK_pچOpOq|BmzBLyFrhD}mNrs6e[3gJ2sR<~^UjO]?xUCqnTzNxtI[dktw\gPi]gaiMncѓՙՕ[euiu8hn2bg6Kk@bJkt?UY5[kAgrA|AtQ7qO5`U1IN/IS8SY?b^C\eDLX6?A2NZ@aaETdJ[iCXiKrlBVI*>C1ES=]yaeorH`ZEY\=RXHgcgvdk0H=0N_;`?Os?CgCnhGbhFadPmgKeoCL`@DX7t?.pK9NOGWKKZTS50r?%R`6>JD(>3',":W/aU*\e=Vr;G~F=mFLR7pS,Rc;RLE2H3<:.Y<%kQ&[c54uK2OA;3!A>&EG0FS3NQ.>Z+BG5;J/;I0BL37X:BY5CX)HX3WV6xc7MB1rN?G3bW&ixcIKEVI,Cn?3fL/J7$J407.I6=G8ET/Oa:?\5OJ7]X)Ic5N_Gd\;np=FgFNe/EgA\j;eQoNgXW|MJfJ;Y?AQ6o[%dZ7t;@kIu~AsRZUXf?Wb7:`1=O,Lr/iEjPOS^S8PA!RK/}s5[/|q#?0)0,84;AN!\T$}F:d4C&/APD$QD?jQ?U)DQ,4J)AR*bl1>nGDP.XtCmmD;n=E6KCSd'FR,?a1Q\1hX2ntFjTiLhbdFOkJ]{qRxKZkj|QuG\fdr8uh@cC`o?N/[zgNcP:Pa8eo@bq\ClԣrĠ]xe`Jci=x@lM|kMA֎ʈ[fhFezqOs>gKcDƀ{wdxzmEkmLu͕tq_fByd4lqŸxQ_/rPcLgdI{^Ozf:|[Ʃ_vؕxtxXLV|^=pdArWHeYinWjNVGWz9qTQz:^0R\zP\d.an+kDdvBJDgz5EHd{|wqTf|]xTtQZY6s]EuKKV~8}Sd`\eiP|ySsgL}dh^`PtejkjR[O/[]4VM)D?%ӯӼ譍_tCtEDH5іt}pqOo_N[h8aX0Lh.T@,}Z7a4\L'0"JN-]nB_{NRu7je9}sFl{MkOX_1QC2^STcOWGrdXmz}cqyQ{iLpcGnqLoxY_oBSl7Rm5VrDw[}^^x`zǔǏʒȓkU`EeE\p>N[7OTW`MUaDIR5@H-5?*2EAV4=T0=\.7U%;D'38$..!3.!68"9B-CZ:JlS,770#)2(!3(!1.$21)D(s;)wd48{X.WZ868BK1LE-KJ*GT2H]:BO>HO2OH3TK65S54F5H@'VA"JB,T=,FD0@>.T;+cC1LT:DX?GF+L9VT0tT4bs?WwR6`A4G-26#10M6OX%/G4C4,8A#9D01?-47%BB)SJ&6cT9KY4Q`6Jc??e;Ei6Uy88h5RE-T(WUC}Uf@W}J|IYm[R`WD|KFOEQm0/T:F4%UE&SR7VS`1e_1iDsXNW@`<=L)G8LF"k9zJ4vV3Q5)E28A"C!u[=on;tVj}wdn[hyKrhouWw_>Y=zFZfԍMlE[BQZT9Z4lN:mc>oQpdYejUGY`Bz\yĂ{rtiNz\aCUaXÄnxdzfvMcw~mnsݥ򨓇`Ѹ连r`uH{OUtR͏v`pxYפ㿒̗`EuYr`_zYS_,}o:D:QAܘsJv~Fas5rDaCzOnvotmhNeb'Da+X{Eufwbb[`cirYpMďzݮ~iPxD{a]sNdcş]Uscr[i;V@+]?(Z=)^PNizmCgT:rO<\KXCrLF{ZSn|ʊnxNj@qkKWD[i8Wj?c~Px̀|cZ{˚ޗƃ߭fy]|fjz`gj{ipLj矾}iw}k]I`]Hi[u]w]|ScIqYN]]xkeUmkehW`Pr\}SpLluJ``9c`ZmɂhTNV9KC-@9>clhyfxQfGmxHRk?sjJ}hAhbPlcWjkJ{XA\N=[ZJdyNjtÊ^qQeGdYNT=Jl0:J%28):<%8F"(6 $60;]HMw]1W;3@<$1-'",7'0,D.b1 d\/MaQPhSMZC1]DCH4EG(LU+D]8;G4DH+IK+KN/@W23J1H7%JHSQ0_L2bX3lF7wO8kU=MH>=K9?L-_K)[_8~[\K>A89M(/R-7:$CF"4R,55->7 _D#?\/2RC<=,:G(G8)HL%<[?.;3!;+&2B2gI}f(oAVZ]|[iMcNGwGJU5;j4:bC?O+VDwI%X}:MfWKL8He9bmJJwB7U8Ha4Ai>PY:Hj4Za:o|BhPR>SZ0if=s>ycxnb_uku_Kid`oELDcHLV;\gY55V)=E"a%S`2}_)J4.3oCWGOdH8N19H)EW5U^5Og4Rg:HQ2P,z^MgPmHq_2BS6NW'brDcYdr\}]EkW:0)M0^`rL]KVX]7VuI]q@qT/y?uYсT}e^IsNrx}w{{_sЃ~€y}ͩr^KmQd<]@քvx~}lGɟvޢr̔ћqSٱȎ\kXYY͋}epgs\uU}^oZJa9MIl=lcQsRZwTdYGgSd|8aάoߪ嶈Ȣ_|`Jh_LВٍ|vrZ~chWpT4[Rov@VTnGn@{jk>l:Ӄr 蟃ptJesFxuSngxWG^|JK`sEchif}Wy~L\snҒp]JWo*Ss/VR,MX2lGm~ى~鄐~φpM{D]EgO\x:k|GewCViEYgul`a`CufcCHE0I]/Jc9Ij4Ad)]d1nχkՍeo׉Ԋm{~˞uPYCU}Άy_oiQwWwj@_L9|OBV?UJZ>kWLvXkLcu@NO;NC105I*3;.8/%;;%+C([4+\V(;[=MF=OT6uA8VD1X76K:/Q>4gQ1kc8]f>k`Er]>*mH/CG:+"H;NE&PB"CD.:B-N?*lH/PeB2WB.6:/-%60!?32O)47-)7#.6!::?DSI"rN$`r:TpSYaD\i?Dt>GY5TW2@sB@_DOZ6a\-PzXbBmiEkwFmDrNu_QhVG9G:?1+`J$`=BuGR\6s;rNmkheQtT8S?:L&G[/O](K`6`h@UڂЎq^d|ZT\~owqDzYyXcyFWuAIP2@m1CI4EF/QY4f9[]\][uLZ}K6sD0G&?Bxg,nR>^2F-/:.d: dE;oNFG*PGKZ4L_-Be3Ep3F]/uh4VYazGEuWPI0H@'hE$w|AP]>nk>cdCLEK="pm>g}bWhlUXrfؑc̭VTOςULlbhoK}okwoh`q}~pfZlm`O]mVb`?x\mf̈́mbguCȕs~|w^DۑlÎ_mLiG~UŌt}wDQa̤x}csYbz_nZiw>n}yYrm˶lsFa]}`XMӿԝqꚀwl}I|jwԁi|gpTT5PE,pf`SLJprӗҩz`Gc<әWӂs՘衅yvfieo؀\OU^dj^husa^YlƬ{}ʹŕrYc|IHj,_w.Xn0lnBtbx֌heXXfȵr؀SnPOY)p`ClOaDqT]YPeqxGpuQ`lR4T]9Lc5Y~M]t;Hs2Ir.dih`kו|m{”jhMo`Qzs|ЀihxSbcJ_SSkXW{latl]l^k^Ty~blKifNo^HdS8ZM/dUFlUp[|OsGxCzMlĉspB_H8zhWdb|OdPHcv@]GMz=7=!UA7[zRxJ|LrSeThhgGwZzꖯ菦ҏÉ׭ه^|auo}be{Z{t]VYY'50&QI>lzPjPyn}{m{hh_tjxCV_2W[L}ڞ݉dnvE`dA`^Am[;XRAvYD_cIbmE^^CUO;QXAZvixd}[yRqbUnM^o]wUrXaLZLXEV@YEYBQw=Ai>QdHspSWI4k[=ZwI8rV#B5=T*0T8+D14B(A@(7R.ZA2TO3MhFXcDgcBFRB@3D7e[.!jP")49%0:42%@<#DD#UH-|M7CuT5JU2>1)5*')#7&9@1H5)71-6!<4#,M*>9!I>ND$HI/KK-RO-Ck5JV.TY1Ph>FyJ@YB_X(Es6PPAl8KXHZKl`;gJCk]NS6V\/4{:LK2|^&]DIUoc:ww=`|DdbESm1D{N\9wOZ}g~Q1u~BnT[VK}YTlGceDQjARcF[h:D^5BH:MC%5O,7G1YH#Tx/?b=TW0fp?xB~MUx;~e2X6@E'Q_}rO|QdLnfALӀrk`RWocmivYڒfc}Xwb;J|m@uE_Η~f«|IpPPɒNX~@TyfZpcǘXchw:yBSBfnIHhΡn͖_҇wxtPx}ېՏqhiW\704EI%ievKՐgͰީeOk>>MWlfX[{]s{X}JcyQ{Ӄᔶنf҈rbob|Zbpo[p]ؕ\TxYLl:_h.Rq3db3oS0_CeZoQezDGqBPq1lPFe[|SiJ\amdlndF|j9{wAixHidBkU3_km;WZ=@%68/X[;S[+HW'GX%GR+hg9^r6ZNaxD[R܇{x›ᦵ䒒yz֖dhch}GM{02W&9$LS:W{YobNxxGslBdeH^vl`\MVtEuj‘я[uA^oKnqPhtE^kMykQlqM^uJvw\{uHbkF^}e|dWShy[dQ`I]?Ru;Ts?Wl@SkJrNmLmIcKWE[F[{:Va3FG3H\GPI,|L.tX*Ne36ZJ,C-::(W@Jn3FZIIJ=GR9UQARZA\WApM:fuFwuaYPCpZiBH~I4fZ;]V>]XB`bCS`F1X;Z4(fC"O7224K=@^0A]BAaYf/D7-J*+=+42%I< 7V/7A?7I+3:));,86!ECE?*VQ.Bb7HI5B[-?_1?O-WQ3Nv@@qP`e>Ey?kR<`l5=O?Z<+[-MyJ;XSFN3W\0N7IoBeg0w9YQ`QSyJES>GN%fF-Q;sFXeAoiGE2Wg:muAiCdW\UQuQfjKNxCLbFXV]IQgRWW:3sC4I3KN)v|6M]IlX~IExKB]4Hg8H[7ps0RLb\9rv9yMdSbUNQ]Gs]YhXAq>l:'hzISؖ魣̓^XH\/EP*eX3ZC΋Qӏς}znhU^IdQyTq̊͑eylhdNrMsmtO7lY7bAi`QzI|pudrTaFcA+RFs鲄\^?tQ`pjmlFeh7tDܔi}h⤟jft=qOpFZfa][ϱ~oɣʑkۏvMt`widqI\o7};e`[]eT:iNx}qّn[[RxS*> 2I$Pk4WrHRzJxTɏ͸ΜkVfdw3h_:kQ{kpĉnuߑ陳ݖیɎ|izXe{YzYahZ>sNvWLO+p`CdZIQ7>J*:2&ZaaKXyALq;Qg3Wk5tzOMmsN_8l,Cn>}>LtSVBTt@rfpeWh{}Wz|SwPT{Nai\Tw6Wd3^`2r{=e>Xx.H^+H_0Cb1\pBee8?T4>U6U`,A_(;Z+3O".K$9] =d(\t7oGzFhWvovqy‹ѣۖʒȇÄőӖpea}kmTtP[:Ap,<`6Upgp}VyTQUYPmxNp]g^ʃhchɖʄZeE]~a}d{UiRkpYkmOwqOiHsh=ZnJ_ix\dD[rEdjWj|HjDs9V^&9E)9D;L]Oha{[d]h^hR\CWm1?? *75:E&'G7 O4'i?/d{Gq^uzcisZnLKyI7OT?;D-FA!L@$7Q9=<6?I,2A-::&6:![7"RZ+KsAGxCAiCEL7BV-LL)X]8]yJc}UOSXcJ4N217$L4rb+LrACQ9J]5V]R_5Xi8azBHreo`{WfV\J_c=|n;Dh[cX]EaA{?kF{{GeLJQAcLFW05\6=C,\N,za;MZQ]nNTnGnW6b>\rF[rTUM48qDDB-KM"s4`fpVNnVGK4KU2QvEDE_kBdjA\G9c@XK+Y:MiFTY.A]'GC(LJ%OY3Y[8S|Dvl;pUkeXbVTm`I{sڢ˨tjdS~O@a+[S2joCmbLmFlqmZUlYt[npvā`hmU`[ގnbz`kcBlY1^E/L҈ppgMmkF@0TvQ`Ah@yY{}ԩb|LtoUu0cV,QGFO2Pf?Qa7\=dDvMSõߛnýtƁӗ_S_{ɀu|wMtta[wOa~DpWd`VpQb?YdHTc71O1Q7#~X:_ldrxmtkr\}mKJp+Cj)g@`1U~-Nmo_q^ucuafct`Zxevcs\}jҤ✝Q`M1t<;b藨uůcOpT|R|Rf[c|ZxoiMeAwdUo“xmdWl{Q_WumJ~o\kanwXqSxSzY{]ƛeft8Na2Me6Wb9am=Qp0Og4Oi7Q\5Qc4LM-gQ3_`=Oc0Dg-F[(;W+;X(?`+@e&B\!Oq%a9qV]ZXlu΋rȒөؠը٢בłȐҙɋi{[rovimzVS6T=RcUt\|zvwR^}Ih{\vuƈᕵՃ|ߟԌgdRdsm`~StuTnkLok=aZ4N_BR{TigyiUo=Nj0K\2_R2\_5Uw/Gl02X46]FJumʄuwo{^kQXz?-Q3@8'^<3&1:-1&I1!}D+sF]]Wg`KOTDOEIK8?Z90PM30<>7(:F)NN0jQ3ao:qdEzdCwpG{dHojJZI^BjL|_cdZQVQ?AjC.W9jlC]^VL[H6EB,4,RA&]b5Vc@f\8\3g>uMkpymgWigGOoTGpNehI>wJ9?A8>'8CN7BS)E[A7U;1F20<(D?(OK,MX+`Z2^C@Q=mT;[@D;1VI,Yn>gpLUL3lU6KC4@-tT)XD.^R82$G=RN'jd.E{A^X6@nXJ]JcU@d=G_5[]2vEQRƃF_]MS1DY2FN3BZ1OO?]'lM;d>MOHC&KV;Sb0Al9GE+]^3Ur3VyOJ\R[X_Q}Ra{oyaFz_|_FAUP&DyIUR>5cB;?'IS&|:Z^FRB<\,@E6n`2pKYbHhIIa)Nh6@[:NX.Tf09X5?L%?i96S$GI%G_2_R1Dk5YY-sQtd\vדPI:v\*[WyW=d8UO*\c6XmFdhFxF~ۓnu~wqcguwmi{uwmnjGGz}NCW\ɾYkD_c/zrGsdX\Q_7@C&f?&j>xktJsQܙbaP9_U(_fFTK9lh@`QσXqwG~RWV`T9[@}dZhuyIs̞mPP^lTkBY`B]zI\qn$GmLo"fy3s4Un6f`Gvtt˴⭶ۯ椿띲܉r|lXzRt\]Ztǁu^fg{emiy:QSAzoinOfNem}†rsЏљkEj|FexdwlQrU{WxoPja4MU7NoRegues_uR^~@Mn2>\,GU*ML(Pa-Mp5=g?QtWjۗyƅց||r{gjUo]DI(,R/79/T>.WK2_D?YN;DdBJdGS]LaKCOSAPL@3J98B4CH0AL2DR8VR3PR1WM0WD6dS7KU9x<:}W3~aEoXOyaVWQfQ>oiFikO~eLmbKLgOEFDYP+FmA^QHlJ2UN6iJ9W7uNoUǏ@erDD6[I+tb<[T>v`Fi?FZ@dY3gX*ic7djD\HD[:fTLK7W`0^xR^Q\zOB~MVcFz;:{P1M=08"F,OO!g_(DB}mG^KidQa=N_Ns`\xKZt?shHHĎ=TY:R(3C(JI*I^4GeG`e0er+Wl=6YGID6W7HK-Kl&Z\:OX9Wp:bDoEnHm|IqYEON,LP9MN-[Y6gk@MgXqmRJ]wZxY^{WU_NxMZ[3Js3JP/~b.bO[ZlOs~OzWu]pi\r`tbsUG{|JōWtZO{DT~U4gCME*lu1tNORC`@D_1RZ=zQ|gd_DSOP1Ms4JV0Dm5Jb=5U4T=$>rE:P8JR%Nb4Ga22Q*bT*wWpgz樾ߪnqFk<<_2qU,mxOeuIMa:Vb=e\7tu=prLS;YgthkΆ{xug~luXXt}boTk`ko@zzEm=ȁTYXFacWGh5KZydѨә[zUqYr⬩~Ԍ䎖lWYW>\Bo۪ti֧܋{w~f}ql`pOiU`1tZ5Xd9qf=~mFn[ePaucrJ[lQ0?B/P|8sFarlLoBleD_yEdzJpMxBUL0\O2~hEzzްӖl}_q[zY_li}u^rlJ\OweKxhWySqkHiWgQo{FnrI\pC\hAdnA`kAYh2Rf7YJYE_z7H])GF"+.-#5-:5!=@#?J(Gb3Hs6@s-=t.Tv-Tc-Cb%0>)XadyrvϛrumjkzeebqBXx>]IlCLsCsokux[ubn|EAR9Oiamvmhdti}yucnXlca9Ia;XaW{rTkLs\~Xwc:I_>HrVlhcjEeyKcqAT_2?N-=C$19 6E(H`9NlO5CJ/FL7FJ7QY8WV:yi<~Jif]u]uaKXAwiKvqNoaWdOIPZHmT;nbDRJC:*E:"VX3uh9`LNbQIJcM3lGSYZ\Z`>|[cWDjM8L-.P+@>!DR_P%^:_SvpCd@=S2LS1Nb5FsBCY9NN(cX/P`5QqF>wQKZD_d,[v]jGfEioJo`Arj6wFk``hPb3C:CC#=R0GU#dg+`nApi:[MXoWgf=oWuRwV[Qdq?Kc}hvܛ̥}^m~fLa@I]BZX5ne6ZLom7NvR[c.X[;[Q5qe8E{{oiSޖ荋zaEuDI.mT}[|]ۑs\[9]?iIbiVuV{P^?f{?kJsV]ižo۫epWQpÄzqQmaeto^nqo_tjsWarxPV}nls~V|`uV|=U[xJz\ħbfāGU~M~Oozu^hXqMMV<@Z?JI0Uh:ul>hi9}tR]H"KR![^6mQAG$vV%g0pU,~h5j@o:ҼnuoD=Cr.^֠Tu`O(3F"7V4YyO~YzPGY;s^WQe6=g:-{G;D=hUI`Q@SPGT8\8GHIIQ82?640.M.!>&~u?qkscYa=jiHIk|>L|PU`=kr2SYWmHgg8XuBa\KtY9_xKbNZ7mKAf:TQRC0Na8PfCe`I>S7bSdJ8c@CaifZJv>f4J=BDB]"=Z)CX'n`.nGExT6Q91F+I<vU!mOSq~XVrnzQi?[O`6Xh2??0E=H<(Rd.^k8jrW4CG*ML,Ms0Jk2S\.`d-SrAml=pHrM~qE_mOegJmXzTHraJpP3?.=2 LI)5M2LM.V`';Z9_Z.nr5%XV%^f:C]=[U-?RfGi?@X%5U*E/!Le/taJ]P[MSV}MhzYN@k{Dfg~hy6ZU'So8oe}xkOW[&/3#UJ/SgDdsLdqpQiEt{5Vj1Ia0T^AI!,#15!3?!5>-?K4IS;E]9Bd9G[5=X0+F@oݓxÎƐŇw[qyFR:.-1'3,'6"*-1.J7(bE+R]=GnI4XG::@@A/S@)PS2[^@H]IMPKiX;gvFh`tfomjdYeNla=KDT82OA3RB<_J<NAhJEDN@LF<06&I%$KN&IYE6H;Q57m='Q7lV|VUocmDxJHmGp]=t7ROQYLZf+Oh9WdCmX6i]?PxR`VGM1mLKr[}azfqrOfYe[Y`k^jZhf`[iy/ed.H8CN&>d'BN#X_ i?Q[DUF6U4=V+I`1xf:Rumv]zvxht=TCPJix8M}EF_RGQ;[X4V_Z/Lz;Mp=9b9F:'Qe6CR?Q_5qo7W;T@g4S9M}.bt(5u3UH.OQ+D|G?Z~pQV6`8lDLyxHgsEW;TlrKwm@hI?+~I-\M?_C/pe5_trjHulC͒bݮ|\>qrA_Z4RV6V8dY=dYYI,VB/VH8XaDfg=gd6ncDv}QuQ_@Wg/}QT|TbpDOkJV`rot_V|Šonoaqv\sObmGcuFbfF[F]HlaRtYXsyElrTNl{Stx\iqKRhAQlCVzIm[qgitd|r|ύ~{Ё}لeWGg8\zEiubvzxԘ퇫vomQ@~:4Y)0H(NR4b|MrׁoOtbu{iX_>WW~n?cd/co-Qh:fdM}dNYS06H(5Q*Gm/Og7ixYl|csRL|;5Y4CW/G^6KQUgzvqWpDk~AU4Om5HV2DM7NaF`F?X.*3!/7'6@2-;2EN2CP3JX=MlJKqHRv;El=_~eyy}seu]zb`.=04@/>:+):&<-1$'H%3D$+C.B.(]9"Qf<2dC2KK828M?,_P0TcBMKIXRCzkBYs~ijcpPpkLRHOF>MG=tA=zF9US?MAA=;.MN1-J,h+-lN'ejG7SC4=CF/-9#Aum`\w|u^hQ^qU`?3C1H7&G_#LQ.TU/Ua@mX?_i>qsUcBzWsceSf[aBR]HnV6gKnz`efHqrMUUyZKlMAlA@mB8g6DP0v_#YOA|c`Q7`^'[q4a~DbRXsUkφPQڽ?fqSI']h.ZC_GZoAErFOtH_uGJ{RFc=Fc:Hf9;b:=J.PX)Ko?TkEUr,Ot8OY/XP+[*_m4i_3c`=YO:YA,n<^eg`Klw?W@l=e;ONXz;_:?^0;\1K]9?D1@J#MA&\s1fmudAcomZDyɔnͥzbcrlQwdpA{Em|cqEYRV0lE奄ɎWDcp-op3o=kYyLaIUk?nМ`щtxX{eo^bqj9iGXn9Xn_Hg?QA9KOBNX2h>]@\vF@Y8vikvdwtxymwdHQxPtGL}^gAj?ǮpulZ_c\V_eiqίېu}Ic?gO|s̖ӣ٤̈|ƸӖQKty7xj4_{DyVvtiב랪΅|˕vhIf_;\i?xX_ljႅSQzF~F„Xb~fg{kz[\hlhtdX]U7LV2Ta?VvE`\4k^hBSeoiouT}W~[{VqYl\jmvm}tox^qjoPesIarNbtP]sOSrKUnGhLp_yl`a~mu<=0C6!;J=oqੲsbgagVUJ@n.8Z3PdÍuvRWTx_WBfrXTyI)Fa6Ne?txSI\i?Kc@Gl:9`/Wi>Za2EY+?X1QoNTT^UTx>G^@mrMrhtr^czSfpXxzftqKK/9A1A6,=@&-G-74)^/).H)$86E))U@$XJ6WP;,zm:uWig^xhe`J|^HYxPV_UW?:WK8QO:NO;bC7RI9nR6j=Ph̍oτF־CoAOCGN2wO.^A>PZHEne5WUMkQP`;H_7PY:InELoBum/MF9xWGV1kT%ug,mBrU^ZufHjLFQQީ2cn=A-NK*Se8Dk:S\2bm0_O]uSSuII|TOtJIvK3P?<:!KEHe5Le?Rf'Ps/Bd;LS-G\&CE/KO'0>'98$u\-yNs^io@MxI_W2Y^1jD{HoAUa1A5F(1L,E5:PW+ex5TRXJX}Emw4zSz[pja]ELQ8SX-Vr'Ty'T'R},K{"Ot)Io$ZF[r4KB=nHLX>kn>fR?xRR1!z9wdk~m|ԗ͌ܪwz`fca}UXOMk=Yl2Ea9=VDGV6\X5s~Ax][adS}YUyNg\XzOTP8AQ6;O2?1G7#[l-ICpp>YXxsAhf@eBZ{T?i>YN,h2Yne~qgVLr`2TT)X^1SQsv^f<3agsXיýhVp5hAq[NWFZW5|S2n`b^eOcWnoEogPwBRlCNiWk\]PJrHOlC_c?vh3TW#JU=]&Fc:jUqaoPIm<9`/WJ-AF5Y;3/D+?55pB.lnEt\UwlWPXP^?>N6Q62^F.cc@EsR4VJ=B,NH&NT1FL6X81Z9+a9,[O6[SD][I]eG>E=`E0iY%HmDHALg>2EX5hJDd;xYqhfVwQ`l]]gIZkJoE'@@-"3"D37Q$RB3YL%cI0HcD[TGzH4f;zNbeҟL?trPXC]R7amAS~CevOJMcInS@k>@Y:GY8DV4Kb7^p%R~A@oM=mEK]2hh1Jl^gVteFidC:T4AO.=`/KX.h[$npC[|KTlD_`=`x;=kG4bG2T5DN)JX'Dl^?bC&Dh4Ff3^Z5Yo8hyPzAmLDY;G6o82V*FQ(LX/b_7^w>dBPFbg0ot8jzHqeDWMQlOOsFSz4Y{(V}0S1W|,V~'Aw&Mg1XS3SpI2]V0r8oMsRCdBKW1U5f;L^4K\5]yCn}Ckr9(<"$2#!K20ggE[AN->G--K&%?#/2;>AtxGVz9YZgkBNx19O,[`Udy_yViy5M]'6T-Kc9TKXMOJ[HqLcMc}Ek~Qi}_rZjUqNwL__cs‡qiaiSMdbjLsXsc~iPYx6HY&FN8M 9W#=e8WmBcwSm`jVNvER\B{DtMiBPc:7DSBwx;6-F82J@4^F;ER>K<6^;-KB'IA(\O4qT:jAUJykIFQJF:DS>9B:VC4TX?4YE-C>=6"9D'QN,O\/BJ7P12l9*XD2U@9hP8F_A3@CG=.eP!EP/Z=:~N1pYDTHqOGhFAmX;]}FJbNj_SdCWZ=.j8.I2!?(##>%1P!Vm{aFojUECYd3s]C/:U(A[5Kj0Zl(ZG`tRN{RPfJro:lRiwZyqJTm?Mb7?C"9W*JT*_\6R`1YgCRg;IeACX9MO&GI+@p9[X=]vAiBx=uEsUZjZdDi?\M@K4]Z,`=MGBu?bb4RNcb?VaDTl=S}cMt}vdaKrU]l}ysiRzlEkvP[[@T@/jI|eδ̪㶀ުhxhdoqsq\iKnu[\QEQZ3M]6_}\akKReF_l=Zx?[U.CS5sM:er``e_aSTVn0o,sw=@lHkVQKtQ}nJzRkjVFoBfl5WSzCPb/EZ0=I+M_+\t<{|EUwuV}]wM{?IROQpO^lovTmtr\YKnUz]QllfD>}87Y>l82[|h̉NX[ecX‹ɯ~lԅрzzr_iwkDOP(@F)qiFRjIkuai<^k-UX3^b?kc;dd@jZ;hS>tc?ogTkxJ\c?GS?@D271"74$6G*$7 "<44tkMY}Be[H\qOJQM}KIl78=(IEP좥SwO^Tj`NCnMSx8;>'ES>oMWJZu4A[:LrPTZolfyOpUoTsJcp?itM~SpSgCY=U|QgyǀQvN|etsfg_bqrrPR`8IT-?='KI/Ur?S@Vv2Nh=_nMjTv_Yz8>Y!0E3M#;`5BsJKyHXpEfSlQr~G]l8Gd0c|@qFW|ELx_vkfIB8QK;NE?PMFRO=DZB:O?.:;4'.?"!O0PO+sK2ve8RtMJ[ELJ7[C8HL>\E?Z[:B]E%C=.B5:A.1AB0YP.WR-hH8l]>F\IKCELD4ID5cB=e6ncJrUiJDX:SU/As?/O:+*!A*GW&M_:Xa=T_9rR;cV5X`KlR@`U<`V@qV6xdALfdHoP\LJ`CV\E|i=aGAsO8X83J.=?-=Q3Ge?Gg=in6jsGKLI;B)PV![l8kT9a\4mpGbdCIn?f4HadBQ4[abInQ^QZiF\sb~6c/W6Y5S{*T{*c~*e>[yHYa8^g3Y6t^Z]aMv\v{~eUdggHjzo_eeoYi_eYm[tp{hlZfLHg&Bwuwc`iOnL\`5c~E\z=AR(Pd-Y;{Q_xsSKYy?bc4~SjE_0zpAXyjs[=wh?t`lmns}E~xGyQCRAk?/Zƥpyٱ٭ݻڰv_sJ~ur[Y`9Na8`f?NtZgc-RJ1NQ4ibBrgFd^@iU[GQ/SK4~[8d;ir6hX7id?_tGgvJ}GPiD^fAg`Ok{Jay:TmBa|L^gHmX>kR;aT>sjO{sfmz`vsSGP1:D4CG7ET4*:!.60TSR`~ILXDmdGut?^e4QG-JF:XUemqNbYr]QpFmYv`kJ@]8H_A\OpIl?^oEG|hƖze]pZhotawH^|2Cb-L[+X[4gs8VHgYwZfmCOp@bcjnlvZW}ASz:AY1CI1Mi7[{E`~?JkESvIY~TmxHnv8Vg,O%EI0YA,EV5HH9LL4ER2OG;]^5WYDNG@ZL:8[3@[@FX9Sb;UlJZtUNqOWKGo?2aJ2OSBJ?=F>/DK6;G2lM0he/wYAU?vcB~WJu]EikPudUtQV`ir[Hq;HPC[`8TrB6}P326D3"JX+Ea;PX:MU/[U1uB,P4wvKTfZMOKVX5XM6rN7bZ9{_IipD]yYZvWdqFOyH=vH2X8:H(GY*Od\4SK)R_4HgBg\7YQSRcm5hi5ZN_NMqAdrDtNH]@WG>M%EP'2I$E=$YR!>^)Ra']f0YH\F>\gwҍ_s\NNO_+Y`)np.bJYSYUNRTd>Qh/[y/d7\;S~5\~,W(W{+X|=9vKSZ2MFrKqOgUncY{b[PMh1VU!MD!?B,i4_qrReSfyEez@fYvVygWvWZZ=_IZNXsI`cFc8dM9P5@T+kq?[_BG=bS0VoHYVdQP{|lrGaj}CU|EAQ%W_/SEmzAcVq{ъ[K_Ffv>nV6cHLC&xX1k=du[Fqlp}qdkhoI{W_\eQjPeƈa˿lUyƪyptzYʇneOvR_YOmFunNgoqiuW|UFb8TXDlWa~ffB-OncGUCDt2?g(=d%Qp4vNSUbOgIcowf]ETsrU_5oY2tUqZaenyOqQYk@_[1ezS;b8nE&|8rLN\ԌY|RXoB^q_xZsz|pw\qima`E[l;s{>Z⃎v}yaNby}yF~Jd}An{FҋlnnUgrR|ԀkjSYgh?uX~tvlvXkgNu{lQzׂ]zTiYԩٛq]sAb]8WyC`HYEnOu}ind[ySoy|rrjVirnwWPcjH`vLp|TrjSlsL|tSxHJV6HB3YI8p^WcE\zU|T|uC}kRusQp~QKgzA^uBbXx~]oVlpNeW:wWOWGQoOcbJhg\Z}ahtSWY7DY44F*PHI]sihHgd|mNyGpQwHc|B^wKRro@XtGsYubl`Dhz8Kp.>_.:\,@a0Dh>hN~@qOjat@MxG\Zyi^qkTgq@q}@Ll;?d;PqE`T`V_L_{FVTh~Fel3@a,9[-3X/=`=>hO^ksQh[p|byb}VwOdt>ftCxCDZGOZ4ZiAfxRxLkU[uWdpFU{FI|OJh@Zd2l;hcUNJsGFq0'mD0A4MJ!Hl9,`=FA*|Q"Z}3W9LI,nU/qt2lYjXj{Jsq;ca8JT4mP$V{0Qn@Qe5aZ)]8GoMMU0aZ8u{?LUEkEFlB7W:1J'9B+7\);R#JV%k`)hp6dDmI>SՆ}zgvGPIe'Fc*L\(b`5|l=}TZgGcCoD]h/]3^@SQ>+f`2v\JkHQI.NS-^N.v:sz{}p~tfdQɞax^{gmxftĵ}xwK;nsxUwrNF3zge{_rXev\dtywiz`Z[3jJmR\c>DoMrS5uLVO6MG)bS.O|:Iy{M`k\UmXc}}Ϡˈn\cCgqAvbi{q_BtqFljD[jYlsVDos>jhFV_h|EcoD}j٧uY^rFcc=XT=h\9fNǕv|SQpwvuUVa9e]HwqhYT\9moCNlDYnz|yYfjYi{fzٗSfxUtB_LrNv|@`nR|qTQh2IZ0Zn9WyDazFnRhUzLbzI^Wf:S]%>W#=Q+@_;WrG\QrXWPd}Nncy_vUlB]v4H`28S-:V%5V/9dD5P9:@;3B3(765,)I/&]A0JWdY8UpGTY@KC6Q52N>*ON4Ga;HE9G@0OX7Gh=E\BLU?[_@_mDh{Vn|ZUZOdMA\P:GL@C:8H<6762.4'CC%QACM*JL2\Q5nQ5lVDvn>_FDhTQ^ICO2IJ)^H,mF&sY4mClgqmYqIn=mIFR8ZZ-t{8zFssOeSV_HHLAPX4epY*?\,OU&SM)Nk=>s6^a6Z>QMNO_yRbNXrVZ]2Qt8@qJ?d7Bi8=a18N'JE$In5Sb5Se(Xp20Ick\AyLGd3Cc$K\,GW2]k9`uAxZ>pLQ`WJi?cmJ9N0D(5H3R#IDlm1a^=zR5@.9I)Ig=Tt4qFsPwZ}OrLmF|MVgrjkfhdlbbǀNhX\5cW-]U)l_2c^.N`/`_7^QbxDq{?ea?P|K\p?d]1KwCOq2^i5|7dSEDLZ6qOw`]jMGM.FV3GY2{]0[>|P~tz|˙fǏrkz^̅i]Ódeۚy^xcyTcmtSW7feF_V:zqASpIiNW6mF-X9`y6nEhQ|k:[tCRhAIU-b_@kCm9xIo[dChqFQi?]\2el?Xh:dqAlnMkcNShBjcBO_Co{_`i>p]7QcRa?RX-\gZtjc[VXq>U_.BZ$Kf4_u:R}PuVu[{x_]gagsCZV8nnHZ`¡ętevB^^<]ZTjm`ikKojT}|\qf}VoTZTt@Kg8uvIWE\n@v}[[tlIpTpoGeTFnfLbiESR=vhjbTanuߥexGO^;T^Daxfi^T\Si{حidO~R{Y}IgMYzUYrOb[nzÂuycscp|OvSdz\XnCa]8[b9H`>OwERr6Nd3>a9VJ`8I`-L^7Vt>]m{=Tt<[rNp@Ab"6N#3M'>X/Xh3TN0SC1AR7TgD_R`\yZnDZo5Nl/C_26S,=J%6Q0QoDBD0DJ8GT<=K@+<6:2/M21hK4cg@qcQkYIgaH>YBG>8AI.P@4b@.U_@DaSPF=_I`X=d];XUAnT 6I.@Q6|Y?KkuyyTuNsOLb.]a-Xc+TY3mS3gb8JVBUO3ck7dFbZkvLdy:ut6ar?]j=SELxPIxM{wHI`V;lU(bC4A5L!6G-N>A \[*iQN[fI`TPWbq>bc9dU3brKPmFVh7mp7Zr>vS8Jyptp}ouu~T?c?P1FV%Im5Rnh[4y\?wȐ͇ߒܴ̕{pt{]zoR[@QWIԂ]ʙɉsagMaIdJ6zX=uYw^uki]c^TVYaqAqF{^mmOZErUwb^gKXw@Pj3]p4UqBU[_@_;XQ\b4_fCGnKWYEqK]N-ad1PfAtfDeN{FQuJ[U6gjKYȩikQoR[iogK^Y}3Mx1xHUc5:#'(#*)7"@$7i72GL+'4/PL[i,Xn2eo9ex2^?mOx}ф윻玵qTyCbr:dhSg>{evqh_}J|WSy5Kc5[MZPsZ̼ݷǃRیXRxhJb`Mgi[lyjozOy_:W_?sxJRgQeTjSk{I[pMmZvzZf`st[e^j]ebV]ha[PD][DUcVxmpLj׳À]yJsrVrAQ~LdJkr@Ie,>[1Un0?G>H:+MO/DP=HJBKM9-L<@=;Y>6RM?VOBLH>;I?D==P=/EJ,]K/V@`yShdUZ@;IH:ZQAZ]?9ZKX:Vh7>h=6L9RM2d^'b`?bJ9VDf5>^2=\0^g/bGWz_yIxNkLWjuUWKT`ZpXZ~\iOdq>_`1R^0Ca/A[,Wi+Pn-w}1[_GxKQj:Qt4Ha6Uo@?n;SQ,DvB;R>MH$Vv17j52P$0B2B5AYc'}WhtSfjen7NVlOK[Kn6]6{z@_yXfQ\jGrK[QuI|KluPsOm\MmGSK7UlA[r2yh?hՋ{ЋjmvldYbJ?mK>ޥ}Śqlpc>Z\2Z`)y_1~AẅƉόǐƍ_plΘsj{VznMknVuv>BrDVcDNv>T\7McAk`8X]Pcr5p?hIamwmmeBPg,hu4jQbhHdg?yISpCj[7WrĬ긖xZsVLe:Vj6@`,8T(;T9l}fuTqm@}vcTqe9^zHj~ZxoZ~O\zf\tD]d\\6VfOd\[|SzVXptnDNk3QA8WK;I[>:TC9EEC>?]E9wV7CnN-IL9/4P9)mR4b[CCaSCP?O0*OG/GZ>EN=AW6MQ>SC:tO8xDZjdd~b[rLD[>IUAJ]pSG`7Nf+@{=Gj?Qg55b:4P;:N+MP2b6uTkaDs6Q|s_pN;lf2U34A$3B$<< TW"P\$H_Cl;9]"6M%8G>Y&Kg3Ku3Lv7s0X`BpIPj3Ou6[q>RuD;U2bh78SB@K%Ec,Nl19_02A$.D"+CB=or3]bzQAU0.D)9>!FA&ZY-JQ0YL1p`4^nEJq5JU&W[,RR0NJ%\t4yZwܣI^l6V_.bg1pCauL_tXv}H_G|c;nvÀvızXsm?rGZT9{WQn9Wr9S}@a}:qF@ZpRdW@NG25*+&,1.j4p7/)?G,yDI:X4GcGlxGqq1~IqvFi:382E-Fc:Pk=Mi3Uo+fBHBOa9@\7H_?Rn6b{\nuMKPTi8mg(r9q]{^qeg>q8T=5S)4C,;AIDHS^59O+FV.N]=V]KZ\4bW=ge7JnSzcJhcFR?y]DH?mKCvfNtLs`Hlw\{wNiaxb]dmvT[3-V00<$CB&SS)N=f4,M'dkHxxeSzs̆XJMc8p}sOuT,I-6P2Vm?Ge4=Q.IS1Xj>Le<[k@j}H{N[vVV`ufbwEtN%]V+FV;~MVd@uqZdtNlWWMSjvupezhcv@b\4BH"A="aP3Zu9L^Exsrnan[eoIn:{~@_q@dWfe]OW}9`vDx뙸䏢ׄ܎zf^ymzjyeqTwdms}so|ihoU`cFrdCqgKxgCthKymO{}PjnRg|Lc~JlyK~rX]_jCrgG\bALe8Ma8PsG`PmV}[}VXWU|^lrVhmY}fbod}mdT~KjU]YSPz~HaiLifncqT[Lg}P^Uybǥʷ٦z·pLeDjSd~LgCNyDFzK[{Jsi4^V%?U+C[DVoDPo::X-2D!<@)I@9TWAOmK^xBL^,3=(5F0CYAGe;Qb=aqG]o:Yd*?R/?R:Na>gr;WT/>M5He6Je7>W70J4/N;BcK[XZ}W_`n}Ɂ]cFWy7;L/DS-HP/2Q58=35B.DI1SJ8Xg>ViMEdUY[SzWN^[MfTGTHDJ<=B?69:535-U;)XT0CEA=;6BI0_L5HXA=Y7?O+uO(v;_RvmYn`clc[DrQ7_ALK62K2KB*EV(:O5hL3MR3=@8FC1[H(f0Y[Ejo.>46(!FAP^+)a<416R:AZ*2O<7B1H*H":^%VM4`q;LrD9qOE];5b1;U1QS(Yr+Xy@dEmCnw?c_:r_4ix@6vLDI;bP&Sw7QPIMjr9SCFk>A]1EW6Xo('/3+6B;P!FF#H\-bY7PPG~`DY4;[6l]/{w9nPtTyMj~xߙ_ˁCbAlATP$i\*bt6>q8KW-Mg1Tr?dJn`mmi~tlz\cRJr8Aj/Pc/Je7?iH!G^+:d/2O%>C(HX-7W1d_*jOJvEHM(@E(UK'MS3iU5L`=eoCTb6Vf1^`:_zJ]uEXG}WLT<_@=K,JN)^V1RHZx8Mb9Ml0zV*M덄pjzWiz|alEx9cgv[|Rf^[wIPf3b^1pMt^u_i]Qgyi|lrA[˟ltRlCuNzb;aғtӘc|]r^|MuZkuFolKuM~IG;iM`gRjxaanTdbO`WLqrTWjDqT0j}XX[@JwhbDJy]6:H4TxPEZF|OjXUimlRHZwYqi\Vs_4OR0gwHBk?Qo>G_+8W0;c9Jc?XkBVsoVqKfNtYk~=ah9GdsL_P:u[7[Z/FB C@,|lZËˈ~҉vlDžkb_|nȎ㟠ޖ{tV]|FVxE`bdgRglQ_|P^lCjlF{hDe;m__]5Wd8gtG~sJ{Ml|Om|Mf{NbtGfuQpworgya{ZTUyPt[l_JhnAtSDyeKw\xfy^xZnZgRr_JY]:{irۓwZgM\N|^}[kTbOfzDjn5Ye,E]3Q`9J\>S_>Uq@bf>WT1KU4IM;JLBaqPgLTp:@U2DL,G>/RG,NQ4Td?fx>Ma8BY3SY?_g?]iB*AP2M\3Bm=ImHelF]Lb\Yw\NgUZOEeR=UTEKDE`@>8P<:8;P;/ZF6CB7<6;?B0AO0;P9ExE0UFTB8UV*NX;W\97Z;E8.9K,UD1~c9IV)R_"'-/"G:J],3M57I/HD(DO1,M007++=!G37V$l]:|r=VQDyLKpUGoA=sJMbFBAe8FX-\S6Czblhf[=K=[IPY0au=euN}hA_jpjRO2vR'2++3/?"3H$?=%=X)HT(RO)ec5fBK_VuDKKe{NuKyPx^[|ȋ}ҏZi9pF5]0>a,Ae4R{;XHbZxYbi_uLnXWzDL^:@`2H_2Jn(Te/Kw5BLAkA?M*GN&=^,9T0:E!W\.Uz@Kv=B\,W`'3P[BiF(?I)LEVB$^`8KTVk?p~InScGdxAbGo\ZPFkKGX2AY-RX-NuOWk@V]1R_/_g,Pszۅo{PiUo8ya4jd6csBxTbOA`=QS,VN1zl:aqqnHMyolq=rJz]OifZy]iiCj㽆šn簂ngFU[zqPumF~yBja2P6FpyT^hFnMtPuYzr\{_sl[qYJmPUS8lgNXlQm7_zGR]1\r4T_1gs84a;BC%UZ/[OLQY]Milw;Ko,Ok.L]3Ll+Pm.a^3tiFgP}ԩduli]kPInq3r\1n\7qfIkNpJjvPf|KxQzzWr}@mEP>]MdS[]CfiGdmNk`alXapxuizhujč|lZuT|[vtkEs[7X,RM1iyI[VNS;b=Hc;Qi^`Md|]nU_oPasQuxLjt>bW8Y[EctWhtUTsFes>Zj9\i;Ro=VzKcC^zJcfpijhnhn<[j-oi7y}JtboeQwwKjzJ_[evgcfPya{^wSxTs@YT:ir\~ҞщӓmechcnmsxpvMbuP]:UV5]`AY{J\yGsyF^v>K[=I]9Oa9PXDdkJYuFW|JdUhObc]qGTZ1E^*>]-^T/CW.Va6ak@bLR\pw\pV`_V\HgSRB?\J4SX7XUHLRAIN@IN9CF7L88CH3AK;VL:AK:8H69A03C/BJ+VUC^)dA,X8.S27W/+`?,xU9\vDDvWK`ENI1G?5EA/G=0MG)AM3^S;myIAsc8=Q8=@24E#/D'0C$G9^Y$]tD_^W{K^rKT~HMcNGV:MR0C`7:g=<_75]25]*]V$Tc0JX;?g2J\9Qd9Wg8MqGToDby?@FCX7FY3cZ<|x;tUYRZ`DLy?JtQOa>`q:fOGt[c^U.I&E8:7;N0Cb6=kIY'.f))@'NAbp3gRXZ@u?4N'[<%lc0Vc\ftMPx?KH-JU(hd3SvSHo=VU/Nl4mq-xBzB}GZyd{\iD\u6Nz@B\y{RlTvb}ZKN~RpX`en`kM^~LuQ_zihYN{DdmXsk{z׉ohāpHbg-F[);M)?N3PcLewEWi=ZeT\KLlEH_@PL7MJ8daHdEXn=U_?Vb=J[>LU/QJ3UcCQ4A9,7G:9J4BK&=X#2H*6/(/&%E\0sA+i70YR9EbPU4@VA6D42<*9D+0B//D+HG+|W*^k:UnFFaDJ[;CW16Y99T1?K&A[,Uh8PbGZYBcp=F_buKrDaJyS6:]uUuVJj42qAKF0:^92>4KG"Sy>CrN@b5]_.xo+\MiPH}igt]SRw}ssfu`lTjYlRYPMl<Mw=Jh=\T0h`+ab8ieHN%6d-)C*A?'ySuCU\?jA6G(7?!>G$W\5Ud>\~KoOZzRVT7VY+7I0:.$PH#k^?^g=`VKLAK7Dc9bR5RY8FR,DW-K]*fh/Us1Dk._^*FfuvZ{]W_y?_~;eNZoFR{O>hNMA-yc?iyNuxE|ZmYYrikSYoGG{PZ^gXxzW~QvX^Jw{phdjibQtfN{pNdR~Ly\ZbXdugpzo|sVTyVXeKWjIx}Yj`K`<\`(Hx:Gr/Hh-Oe9Jg9Zy:Q{ujz;qX{cgbnȍQXSu1gx@eeCiOgfY_=kQ-oD]mcp}Obqzt`{I{DwrH\uyCkzM^yCCS3QN9vAfp_lyp]~i]nZNceAo>x`gFzOCipAMmFRh<~]dgMTkD`u9G_6U_AłQYD0Z/3a:+O.7R4hRIv@tT~o}``s}pfsFpuLy^YbG_z8iBWsVrCK\5IU,GU0M[\GMC\Z@5VU8ThBO[@IJ3`XeaBvc?M?UM-Mm6bdHlkDgOT{KDY80N)Q=tVI[@XKVZ.Fy.Xr<4sC;E=fm9AYRlOTUMxSi_+\y/bxF{~BqHeKRuCgc=z~C{GVgBHp9OO/-Q$6A$ZW#^E]XWR5hA8P+>O)LO)TT)TATE ZU&Lc;6o4;Z0JV(}p/PSEh>Ae(I_)HZ&0`-'D!':065E#JN Al)AE-FS0aH1_c7NsGcY;HyFLlH>xNAc?Bm?Pk>Nx?Ys8R:\zA\VdɂyrOs\cAn}=yNmY\lX`}ZddhTpSe;r9Rm_ZMPL}{A]g^NjgLcJahcZ}[ySUj|i{gHdtI~Okow~w}橀~z}quOt[zQ3DW7R[;FY6Oi-Wt1S?^DkRhMW\RBai.LwqLQjl͉ML=GJ4Na@oi9bwMr^Y۝lZ}wAPyIy?VQ+T\7PZ3\y>g}>CHUgwl[l[Vg]GsH\mipVUpI`pResnLwiJ[kUjNq\mrXsI~uZ`Q\q<{`2fk3xCkHwR\akkT_Wwttg{WqSwWxbkY`CcFaU_Gk=`}XΉeyHfjChc7Oi3Aa9UfPt[n{BN_8[W:MbA\jQhj^jPYnPeGWnCm[JreGrlGUxQOFUFSHWf5BM7NN:f]EwyTQ_e9CX8Wf?Oe-;G)7M8LpERMkJi{>\d?BZ7AL01C3>\F[sS`j=Q_9KG7KS8IN8YZ5Dd?N^Bs[D_z@JwMQgNNc>L[F[[IfdHZeXTYSGVICH?7>B.?E"KO#KW%/G6[>-8k18:7>,(A1'=<.QC.Q\,W`8LO2YF4wK8`AlYMqiYKFWV>UgGGIF8O8@M*FB+H<*VL2@a L@ 6e*)U5KM'cY)Ro:Ic;LP,NP(Qd8JrGMQ:>Q0;X;GD$aZ&gqC[sS=bFC(lUHF:`VDR#\X(`7NIiNqUngmm|Cr_bCc0]J]RKb3Hf4OFSUWKMwJOiF`x@@tOJK1TX,_j9H|FHa;Je3Gb5Bdl`Z>SjFbi;_OjaazCro0x;ReSyxQVYuyKwnHdtHevL~{듒do^ƒ>a0A; E!c;5Sd?pUsdmEjl^=gT֋tuuqpdχ܄IuEeQxYĈ__lkgeuJAU1RZFbh`}gddFgdQuggzbk]LXK;KF3XNk~J4;(5B-9U2V`Af~PhtIn~Dg|K{Vt[ʹvkR>Yn>eg>cuGrhŊΔϓkȕǞˆrWOkU`jO{^MocJjcWyZvKe^HqzU\xKST=YK6[O:ZZJgiUu}MXhK{dXvy>fjt>dy1Ih@VJt|YxyninYdDIq>]lRn^YSMsDIj:I^AV`Knlkr{smiZbMUwIYkMdwSbcyssdfKdQ[{;ML3@X.>P7Z[B~m>a\9DQ/N];Yq77P+4U?[LbKfu;UX)*;E'LL06V1:N3VG.bW0\r=KzJRoMZsKQmQH[OEJI2K=:A8>B>DC2@E.5L5-I42:+71#>7#R@'CI'3A1=>)aK$BR%Z8)S[+'L:.1-<0SG)cJXaN\LJgR9pjMOtUKIHIG8?c=AWAQN:RT8\`CfrJVqLBhG=^?Me;6n<'M:+?!+=$:-6+8I4NZ)Bt8Pi;>\:29+8:JB!MP&IZ/4Q5P/)BM'/WG9C'PL G^>7`I?L=PE2T_-Rh?VIerFN:7eYK)JY$@_/7[.8R,6W)Y*NT,KY7hp-HO@[GA`/3\01@!9G!AP!4C&-?'4@@?Ck'BM-OZ6TeBQDTu7Ug,TI\ml=wJyJVXe[N9Sr?OgiU*LrPvaYnIX[IVkD~hB[oFMq<`v7lo8RjpovZ]:\qyyV~Pdq=~Pap~wxٔ`xfw]m]{dzkevys[~__HtV;T?P@.JX:__AZXAyKWd>KD3U4GJ3`v?PEuU4RP,Nd,~p6lU[ObT-LP.`n5Vn1nf,lJWkHǁqnOTG_+~gUvYVOkFX`=lkEYx֐q^I[-xrMyӖmuy_qbbUDc@ut6r9B<&@0 N3$bJ3y]DDp:DI'[N-o\:vjFwmSboz[oUsQ`eXzy]\}pgiXJ]_Phx`gBnNdtQpV|VPspAfiDq`uWrpWч]E[`7iT5d\8tpQƒՆiVx_pclf|lksXlfb\VtJsvQr|ZgvLidJ]ZOWO9cU@jL`9lS5kYEvMt`9sl@d^=ljSuWaP|}N|OHMvF=^~1[uMZtXZQRyR^X\S8j::K+?:+dM:W`;[l1Wj2X|C]KL|Y`rUh~VZ{_yZxKPr=ZrRu^q\jE[b3RM7_fMq`qkckzWeT^MgqLZhUhvcuTjIe~;ET6SM6GL3AX1B]6IX0@I2?N5Xg;Hn?NsF^wFan;Va+8C/>"8I5maNtOlThczoX>>*7>(9F,AF-4M)%F$?9!cT"mf9hm?iJ]cMpWBK@HJ3NV8GZD;QDN>;AD-Q;$?B)7K/GC0XJ1^_;4`BJA6K[-]SCxX?b6hjEafUT[H[W=q`EuoNOpYL[O[M=zQ8[Hz_FnZF[`ISiHIY=N\9Nf?Lz:8]>1?09Q(6M-7?&]?"UL*4eL8D*QHJd<^nAGyP=SL?<(XE!Tv2RoHel46SCB44b,2O5XJ'_z&;ZMS=Lf$^jGh|?sJYdrsvvhdSXlb5EO6dMAP+s="e=bbMQEm?3R19L/AU0A^6@S/,M+:D!DZ%=a+?Y+:W59[.4Z.;W)FX'F])Hc/;_>5P.SQ'V}9duC[RGyP@g8?R,:G*0D$/68#O,GH*:V=fF$bE`y>p~FTTfR[H0_^2Vg3Vd0nxI^O\o7}EC{YOZ2H*LM[Op2vi3x7{`IdVj;]7VdfrTgFVj9x^2Xj6Pu2ZKgPk~@q^8r?{jуe^T`uDQx{ps\tOV;hLQ{ynaL[MkYuUxAcĚx{pnhPuYC\@_H,dG*|X7Wg5bs=TYN\8Se@dxB]x>Qn=8b5Ya1[s4l|2Im5IZ7JjERo=Ea*Qd+a,SR*N=ÊPmEX[3fesFRN-RS,ZJ,]L/\7qGrr;bÎq=tBwD́نps}hxNV6jq4Wx+c4MzLeHo3FUvU~}TІZoD>M.82.HODXuo٢dOkȕ蝢Њkeh֛킄\qʜpk`mJfFN\C_gF64&88*@M6VQCZ{U]UMEObBZwPQSo[áwZajbVXcdēsƩȡƐw]XnnTg`I|R~pNvf`JRO4NA*HC(EH7vm~bxbVJif|jck{i~xFdP1iF7v\CuxUly\|zdcnmHlX=tOZn8[L0JV@yqZzae^nLqpKtk{bkjc[wIkGdDZv1Nh1_~L{n~umdSzIs}`hdRPV`8bP;LN:O[Om@ElHdAlpL|V|^gH@,EB*1@+0?*/?' 9=53DV:'q[-oDJ`@ePHF6QH/YP8IOA;KAB=164+;6)D;#@H01M1;5.19"/2!24!14D;I@ IMpKD^ATO6Vf9JJBb?=J*OM*IJ%;J$8I$;_*Lo9Gh>IU6_S-OOLJ8uH7WBEJ.0IMHY;\P4JF5kU0@3KFPb+Lo:oj<=^IQK7k23F4JH+ma,QPJ^Me^)XGfdXwvt|pY{V^]6@jPlT+K,@fF2^,U6/p^*Z]JL9]0D^,DX97H);F#:K$7C%;U/@a+9I%:> ;I&AY.8[18V-8X%C[&LX#9d3:T8XV+Sr4gyBTOGlN@j70[0+<%':)79@>; SX$Kx8:eBP\0Uo4Ks2Ag6A\1CE"8V-;C$;L&;E3J%3<NG&Rn7Kk7R]4^r8VMSM\Np`xo{}gP}NoSkp}_HYON=,9I)33"G5>R,g\5XQ]c4~IpZD}RIK7KQ$E`-xg8mRZ}?_g7m~=Qs:M]4W[1l;LMj/I`,Uj8J[?nbZÆPmCea&uh76R59A#HV2Ufjw{tcuy}懲ufȕk|i[{QQ]maɝϖҗǎxhhlЀsbXcȄ剴wyL}=9E6;E:6<3).):+=7"L7+E>/'966&$* -& (85AB fJ%OL5a_3aq:sjD`zQXuWolEivFOyN:uTMUF|U-_[3[T@Y?^ZC^VIWBnOkqc\`]WZMcbASwENpL[lEbpEZ_F:l@8R8:I1/D(6<(1: M>(h[(QF4RIRCtR&aObSTTSSDiG>K=6H*@B"QH'`d:/c=>:/H[3]BV9+^s24W7MM?O&QT.^j9|KRTOj]:lB7KU4=X#+];-9(1:3M%JN)Fc,8h5*B)3tUA?2FJ3MZ8ssJTZWM3y9VQkO4MlCEq6PV,W4R~DXa3^h4frBH^E`V,ee:_yEY2_7Gk4@J!VT!qm9uIX`fm>}HxvÏxY_D`>mOmzEwRpQZjmjnSfpgEroIoDgwPhW<}_BlM`GtZ}ٰٞÀ[Ya~|b[k?y]Ρxq[]mKUY>b^GjU@lZLYvJV}PacKpkLrb:s{FS|KLP5HN2WI7``?`W5{o9_J:i+8GFW%d`6^Z7Qf8Zl2esBdr~rwPMCl3Kn?@`DImBhGeJ__8dgLr[ikmRlRnQ|HxHrgY}Z:\XBW-PY-Oa2pp1|ARL~7S\+c\5P[}Y6C ^71phQo}c\iWuXfb]j|GYpr҉ͺtswU~̀orρupـwsib]gBW]5Z`7a]2d6~B|MpKsQʲiUQcґxnjv|mohwrna|Y|dlsxbqXbptab|[]v\UtuVpRvرqK{\œubzOZw]ylhddWtV~\pUfXV\bVebtt}xth]fxJbs[p]}SlDX8`n4yAPfXw@a:X+Jg=V+R^BxQwE_Tq|n|tvEWL+>8(?ILt~yrrf}yĉuyaXnm{e`cN\_xsv`VyLUkG_g?a_\vV\qNKmJ=c@Kf_݈`bUdgY^Rah}ytnSOy@[mOtkzuhshmdl]jXs`hfk[wjxM^tKUeDRe@Q]3@7+G>(=>+/;/#8).2'33 A5XR)_e:moBYvMTqENpEJh@DS>1D1)7,83&G7"OB(9E0@C3"C0.'%)#0% @&X@'TK*:6&=;,KJ-LL+kR/bv>GiFeV:~u8ixI5gU>CAbM&US)aW7W]=wSHk[DfPxXuj^_aNXcI`rAZhBewJ^VQ~QRnFKK=:Q:7E7+G+(4$13C9ZD%eV/G@F|N~ZBaqHifEwIiSDWCjE9S7L5EW+\W0wp9eIXdC~N;YCFO.cl=`Onhcvr{Yv]Lf=VU(`g$CBVIEW94o>@5HL@w8IY3`j;cHarXGI8e:@P)1k1(N?9;&NV*Kh?@h4;W2)G&.97@3B)(6",47;>J J["Hr6=l;K^,Md)Zd3[E?lMF<*6P'/X'.E& /&#96L['^wBW~F9o@>N$S^"L~?;c:BL(O^;;T8=R)1F$9I CQ(KN*\j6Ja9Vj/a=`zct5eb6fX/o8mIY}q}RJjex{Im\TreuiIpY=mN5wQ6sH}HT]ѢizOy]ored]a^ux?wr7Cjj7hC_u]X|W_[Cd\1MNA[ACF0\<-S`JJA1SL4\y<<`,/R!INiT*iGjgecq]kłŅhlmǕt{eVpVlzzgrAu9w=fC}aDko6yGjVab;wAIp:meE@j;ZG*ry5Mq>Wi7DH5RME[tG^C0jVWU13N6]]Qso}zumtcNpLQdnrlfcax_xyniuŎԗ졈ۓэv`ieMiQ8YNE`hOkSbwIboMVeWhsz㞀̨~Фi\_^_ژdѐcrQwlHnkBd\=r\5qc>ojJlwXmljY}ht~KRjPɳوZprDOnGMmU~^UO=f}Ucfz[WMtz@kYdyg~xwov}Ʃ̜lSvGon]Y܆fffp{}xqvzbeMR{L_Y{a|gtsty}scom}geHJf;K[Ele@K`6>:*E<)5?.+6(7=+1F+97)@<%JN(MZ0E_8O^8J]7Ia>=]>9'\V*7c4>D7WL&Tc'=d11K12F2PO0^W4UZ7eZ@ibP_QpWok_\fU`pTZPciNqt@d~I`sX_~QVkLLR@>J91H7'A,'6".5>4<1 H1!`S#[t>sLCxQ\]HouC^JdpEQxD7xD,P7;A$Ea/1S5<;'ZG=`3E]=NP5VS1@f>AK7CZ+GS.cb-}?n`Od@c_RnW}^|o{keS`pRKnA>[;?b,VS&Sr-gtOeJ.n*98GC&In9Qd:[i@[v@OoHKi;1`9@P,E^,8qL]dPH^HKg<:k:4R/,K+,98?!.D$'*423C&1D%L=#Pa)K~QH>[_;tX7m6Tcd\P{MWKFf?KV-Si+Z};CM/M+E4![W0\Z<}Ef_eRcI}>ljTsRY6VX,Ye/u9d~Cv=QxYmLo|EbR|R_Ki=_B^eD`N?QNCoXHV~KSTT_阎lm߽~ϦTuu=jc@ns=cj7]q6wa1zL$b?WeH[^1c@DfEJO?[S9qsXmN?NT=Ju<@\2:g.Fj+^p.@]q?\d\x^ˋǟnn|GGMm?W?O~5Wf%k^%fuM6cj0i=CRq?qr?_G~Jjk1KT.lhDUhzQ\oMxbHhQ7Q\EXJ>sVCK6nqNmnĚ{ofvZxTtF]i_YxEyxHzNZb]yUip~휴ٌxl{E|R{yY\Yr~V]jğuӣw͂VtHtM~ZĕnjYLJVVc~MzkDek7OX3EF/QD1iYIɦ̜n~jzhʛ婓҉ekZcfjlo|ȋ}y,R6XVBvdiW@t}:VqEg[jpwŕ|}p`av=d`8gc8UY0\]?qyVPNFl>&4G"8PoKki~~{zpod_]Xjiwlwqjm`uVbFS|L\^gnnl~gh]UANjTxle{d[pbVsZNo[cln`f[ww~tzv|{~mvVlH`oBVX4RP6WSHk{cubukwse}Qe_|gdS_{INgEdlET^;6>-2F">V/?S5G[5LbC6HR9Hk>=T.NQ(Xd-Ci@EU8Ba-MU2eg6IO`NeIOrPCnN1`4/K./:!7A":'1)C<3G(:C&77P7Vf-NB=vKW_8a>@P+U@NG+Hj3/b9!JD 3*O:N`&Fa4R`4Kf,Th@a@Sm2sw2YQAyVHcDQtCF`>hN.ys2iBf@mQVdN[VhEs;aDSi8HzVf{W{jpYbuFvJfEjoC]lPfnTwxUnZedrsy{K?Ā;LlٽڶfhlD~tD`q2aY;HSeZFjQ`J5SM2Uhjxg杫ޑud_ƍWd9ymBtv{pqsnd_m^x[ZUP_h]YxIsfEXmL_d?dh>^V9QU7_cXʜʥӟ쥞⛵ꜿݢӟ߯忲䩍ѓ{Ă~~pxeQzZfQoN}]Zs:W^&V\4^Zxzxv~Đȋmyc{}yWmpH}Iew;cfAZ`6QM-ZS;geF{UXGV`6JQ.J>0LK/BW6Eo=Dr3\w4ln+H]*DT/A[5@R//A/7836."E6&YD/NI2OD6FX=KhEh`|xn}eh]c[cWpYm]z\cOkKRWXd]knrzlzWgEKjEd||}jobbLi_VfZThQi{_fS_Tf^tfrc{jXwQXq5GZ*=> /5 CC/Q]Dj[pam`sPcESvGiRv~FhbC`JxEKYr< \ No newline at end of file diff --git a/SD-VBS/common/toolbox/lagrcv/test/pathdef.m b/SD-VBS/common/toolbox/lagrcv/test/pathdef.m new file mode 100755 index 0000000..7a33e83 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/test/pathdef.m @@ -0,0 +1,284 @@ +function p = pathdef +%PATHDEF Search path defaults. +% PATHDEF returns a string that can be used as input to MATLABPATH +% in order to set the path. + + +% Copyright 1984-2002 The MathWorks, Inc. +% $Revision: 1.4.2.1 $ $Date: 2003/01/16 12:51:34 $ + + +% DO NOT MODIFY THIS FILE. IT IS AN AUTOGENERATED FILE. +% EDITING MAY CAUSE THE FILE TO BECOME UNREADABLE TO +% THE PATHTOOL AND THE INSTALLER. + +p = [... +%%% BEGIN ENTRIES %%% + '/u/ikkjin/Benchmark:', ... + '/u/ikkjin/Benchmark/viola_jones/src/test:', ... + '/u/ikkjin/Benchmark/viola_jones/src/test/distrib:', ... + '/u/ikkjin/Benchmark/viola_jones/src/test/src:', ... + '/u/ikkjin/Benchmark/Toolbox:', ... + '/u/ikkjin/Benchmark/Toolbox/Gang:', ... + '/u/ikkjin/Benchmark/Toolbox/MultiNcut:', ... + '/u/ikkjin/Benchmark/Toolbox/MultiNcut3D:', ... + '/u/ikkjin/Benchmark/Toolbox/QihuiTool:', ... + '/u/ikkjin/Benchmark/Toolbox/QihuiTool/NNMF:', ... + '/u/ikkjin/Benchmark/Toolbox/QihuiTool/RGB2Lab:', ... + '/u/ikkjin/Benchmark/Toolbox/QihuiTool/SIFT_toolbox:', ... + '/u/ikkjin/Benchmark/Toolbox/QihuiTool/SIFT_toolbox/images:', ... + '/u/ikkjin/Benchmark/Toolbox/ShapeContext:', ... + '/u/ikkjin/Benchmark/Toolbox/ShapeContext/lap:', ... + '/u/ikkjin/Benchmark/Toolbox/ShapeContext/lap/CVS:', ... + '/u/ikkjin/Benchmark/Toolbox/ikkjin:', ... + '/u/ikkjin/Benchmark/Toolbox/interContourCutsAffine:', ... + '/u/ikkjin/Benchmark/Toolbox/interContourCutsAffine/source:', ... + '/u/ikkjin/Benchmark/Toolbox/lagrcv:', ... + '/u/ikkjin/Benchmark/Toolbox/lagrcv/test:', ... + '/u/ikkjin/Benchmark/Toolbox/lagrcv/test/result:', ... + '/u/ikkjin/Benchmark/Toolbox/textons:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/TOOLBOX_calib:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/affine:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/calib:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/calib/TOOLBOX_calib:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/calib_bouguetj:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/calib_bouguetj2:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/common:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/disp:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/fact:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/filter:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/filter_hist:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/filtersQuad:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/io:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/matching:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/matching/pub:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/matching/pub/contrib:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/matching/pub/contrib/v5:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/matching/pub/contrib/v5/optim:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/ncut:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/pyramid:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/stella:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/tars:', ... + '/u/ikkjin/Benchmark/Toolbox/toolbox_basic/textons:', ... + '/u/ikkjin/Benchmark/color2gray:', ... + '/u/ikkjin/Benchmark/color2gray/src:', ... + '/u/ikkjin/Benchmark/color2gray/src/TestImages:', ... + '/u/ikkjin/Benchmark/color2gray/src/colorspaces:', ... + '/u/ikkjin/Benchmark/color2gray/src/helper:', ... + '/u/ikkjin/Benchmark/disparity:', ... + '/u/ikkjin/Benchmark/disparity/result:', ... + '/u/ikkjin/Benchmark/disparity/src:', ... + '/u/ikkjin/Benchmark/localization:', ... + '/u/ikkjin/Benchmark/localization/result:', ... + '/u/ikkjin/Benchmark/localization/src:', ... + '/u/ikkjin/Benchmark/registration:', ... + '/u/ikkjin/Benchmark/registration/result:', ... + '/u/ikkjin/Benchmark/registration/src:', ... + '/u/ikkjin/Benchmark/stitch:', ... + '/u/ikkjin/Benchmark/stitch/result:', ... + '/u/ikkjin/Benchmark/stitch/src:', ... + '/u/ikkjin/Benchmark/texture_synthesis:', ... + '/u/ikkjin/Benchmark/texture_synthesis/MEX:', ... + '/u/ikkjin/Benchmark/tracking:', ... + '/u/ikkjin/Benchmark/tracking/result:', ... + '/u/ikkjin/Benchmark/tracking/src:', ... + '/u/ikkjin/Benchmark/viola_jones:', ... + '/u/ikkjin/Benchmark/viola_jones/result:', ... + '/u/ikkjin/Benchmark/viola_jones/src:', ... + '/u/ikkjin/Benchmark/viola_jones/src/Additional Functions:', ... + '/u/ikkjin/Benchmark/viola_jones/src/Code:', ... + '/u/ikkjin/Benchmark/viola_jones/src/MatlabFaceDetect Files:', ... + matlabroot,'/toolbox/matlab/general:', ... + matlabroot,'/toolbox/matlab/ops:', ... + matlabroot,'/toolbox/matlab/lang:', ... + matlabroot,'/toolbox/matlab/elmat:', ... + matlabroot,'/toolbox/matlab/elfun:', ... + matlabroot,'/toolbox/matlab/specfun:', ... + matlabroot,'/toolbox/matlab/matfun:', ... + matlabroot,'/toolbox/matlab/datafun:', ... + matlabroot,'/toolbox/matlab/polyfun:', ... + matlabroot,'/toolbox/matlab/funfun:', ... + matlabroot,'/toolbox/matlab/sparfun:', ... + matlabroot,'/toolbox/matlab/scribe:', ... + matlabroot,'/toolbox/matlab/graph2d:', ... + matlabroot,'/toolbox/matlab/graph3d:', ... + matlabroot,'/toolbox/matlab/specgraph:', ... + matlabroot,'/toolbox/matlab/graphics:', ... + matlabroot,'/toolbox/matlab/uitools:', ... + matlabroot,'/toolbox/matlab/strfun:', ... + matlabroot,'/toolbox/matlab/imagesci:', ... + matlabroot,'/toolbox/matlab/iofun:', ... + matlabroot,'/toolbox/matlab/audiovideo:', ... + matlabroot,'/toolbox/matlab/timefun:', ... + matlabroot,'/toolbox/matlab/datatypes:', ... + matlabroot,'/toolbox/matlab/verctrl:', ... + matlabroot,'/toolbox/matlab/codetools:', ... + matlabroot,'/toolbox/matlab/helptools:', ... + matlabroot,'/toolbox/matlab/demos:', ... + matlabroot,'/toolbox/matlab/timeseries:', ... + matlabroot,'/toolbox/matlab/hds:', ... + matlabroot,'/toolbox/matlab/guide:', ... + matlabroot,'/toolbox/matlab/plottools:', ... + matlabroot,'/toolbox/local:', ... + matlabroot,'/toolbox/shared/controllib:', ... + matlabroot,'/toolbox/simulink/simulink:', ... + matlabroot,'/toolbox/simulink/blocks:', ... + matlabroot,'/toolbox/simulink/components:', ... + matlabroot,'/toolbox/simulink/fixedandfloat:', ... + matlabroot,'/toolbox/simulink/fixedandfloat/fxpdemos:', ... + matlabroot,'/toolbox/simulink/fixedandfloat/obsolete:', ... + matlabroot,'/toolbox/simulink/simdemos:', ... + matlabroot,'/toolbox/simulink/simdemos/aerospace:', ... + matlabroot,'/toolbox/simulink/simdemos/automotive:', ... + matlabroot,'/toolbox/simulink/simdemos/simfeatures:', ... + matlabroot,'/toolbox/simulink/simdemos/simgeneral:', ... + matlabroot,'/toolbox/simulink/dee:', ... + matlabroot,'/toolbox/shared/dastudio:', ... + matlabroot,'/toolbox/shared/glue:', ... + matlabroot,'/toolbox/stateflow/stateflow:', ... + matlabroot,'/toolbox/rtw/rtw:', ... + matlabroot,'/toolbox/simulink/simulink/modeladvisor:', ... + matlabroot,'/toolbox/simulink/simulink/modeladvisor/fixpt:', ... + matlabroot,'/toolbox/simulink/simulink/MPlayIO:', ... + matlabroot,'/toolbox/simulink/simulink/dataobjectwizard:', ... + matlabroot,'/toolbox/shared/fixedpointlib:', ... + matlabroot,'/toolbox/simulink/dataimportexport:', ... + matlabroot,'/toolbox/shared/hdlshared:', ... + matlabroot,'/toolbox/rtw/rtwdemos:', ... + matlabroot,'/toolbox/rtw/rtwdemos/rsimdemos:', ... + matlabroot,'/toolbox/rtw/targets/asap2/asap2:', ... + matlabroot,'/toolbox/rtw/targets/asap2/asap2/user:', ... + matlabroot,'/toolbox/rtw/targets/common/can/blocks:', ... + matlabroot,'/toolbox/rtw/targets/common/configuration/resource:', ... + matlabroot,'/toolbox/rtw/targets/common/tgtcommon:', ... + matlabroot,'/toolbox/stateflow/sfdemos:', ... + matlabroot,'/toolbox/stateflow/coder:', ... + matlabroot,'/toolbox/bioinfo/bioinfo:', ... + matlabroot,'/toolbox/bioinfo/biolearning:', ... + matlabroot,'/toolbox/bioinfo/microarray:', ... + matlabroot,'/toolbox/bioinfo/mass_spec:', ... + matlabroot,'/toolbox/bioinfo/proteins:', ... + matlabroot,'/toolbox/bioinfo/biomatrices:', ... + matlabroot,'/toolbox/bioinfo/biodemos:', ... + matlabroot,'/toolbox/bioinfo/graphtheory:', ... + matlabroot,'/toolbox/commblks/commblks:', ... + matlabroot,'/toolbox/commblks/commmasks:', ... + matlabroot,'/toolbox/commblks/commmex:', ... + matlabroot,'/toolbox/commblks/commblksdemos:', ... + matlabroot,'/toolbox/commblks/commblksobsolete/v3:', ... + matlabroot,'/toolbox/commblks/commblksobsolete/v2p5:', ... + matlabroot,'/toolbox/commblks/commblksobsolete/v2:', ... + matlabroot,'/toolbox/comm/comm:', ... + matlabroot,'/toolbox/comm/commdemos:', ... + matlabroot,'/toolbox/comm/commdemos/commdocdemos:', ... + matlabroot,'/toolbox/comm/commobsolete:', ... + matlabroot,'/toolbox/compiler:', ... + matlabroot,'/toolbox/control/control:', ... + matlabroot,'/toolbox/control/ctrlguis:', ... + matlabroot,'/toolbox/control/ctrlobsolete:', ... + matlabroot,'/toolbox/control/ctrlutil:', ... + matlabroot,'/toolbox/control/ctrldemos:', ... + matlabroot,'/toolbox/shared/slcontrollib:', ... + matlabroot,'/toolbox/curvefit/curvefit:', ... + matlabroot,'/toolbox/curvefit/cftoolgui:', ... + matlabroot,'/toolbox/shared/optimlib:', ... + matlabroot,'/toolbox/dspblks/dspblks:', ... + matlabroot,'/toolbox/dspblks/dspmasks:', ... + matlabroot,'/toolbox/dspblks/dspmex:', ... + matlabroot,'/toolbox/dspblks/dspdemos:', ... + matlabroot,'/toolbox/shared/filterdesignlib:', ... + matlabroot,'/toolbox/rtw/targets/ecoder:', ... + matlabroot,'/toolbox/rtw/targets/ecoder/ecoderdemos:', ... + matlabroot,'/toolbox/rtw/targets/mpt:', ... + matlabroot,'/toolbox/rtw/targets/mpt/mpt:', ... + matlabroot,'/toolbox/rtw/targets/mpt/user_specific:', ... + matlabroot,'/toolbox/fixedpoint/fixedpoint:', ... + matlabroot,'/toolbox/fixedpoint/fidemos:', ... + matlabroot,'/toolbox/fixedpoint/fimex:', ... + matlabroot,'/toolbox/fixpoint:', ... + matlabroot,'/toolbox/gads:', ... + matlabroot,'/toolbox/gads/gads:', ... + matlabroot,'/toolbox/gads/gadsdemos:', ... + matlabroot,'/toolbox/ident/ident:', ... + matlabroot,'/toolbox/ident/idobsolete:', ... + matlabroot,'/toolbox/ident/idguis:', ... + matlabroot,'/toolbox/ident/idutils:', ... + matlabroot,'/toolbox/ident/iddemos:', ... + matlabroot,'/toolbox/ident/idhelp:', ... + matlabroot,'/toolbox/images/images:', ... + matlabroot,'/toolbox/images/imuitools:', ... + matlabroot,'/toolbox/images/imdemos:', ... + matlabroot,'/toolbox/images/iptutils:', ... + matlabroot,'/toolbox/shared/imageslib:', ... + matlabroot,'/toolbox/images/medformats:', ... + matlabroot,'/toolbox/instrument/instrument:', ... + matlabroot,'/toolbox/instrument/instrumentdemos:', ... + matlabroot,'/toolbox/map/map:', ... + matlabroot,'/toolbox/map/mapdemos:', ... + matlabroot,'/toolbox/map/mapdisp:', ... + matlabroot,'/toolbox/map/mapformats:', ... + matlabroot,'/toolbox/map/mapproj:', ... + matlabroot,'/toolbox/shared/mapgeodesy:', ... + matlabroot,'/toolbox/slvnv/simcoverage:', ... + matlabroot,'/toolbox/nnet:', ... + matlabroot,'/toolbox/nnet/nncontrol:', ... + matlabroot,'/toolbox/nnet/nndemos:', ... + matlabroot,'/toolbox/nnet/nnet:', ... + matlabroot,'/toolbox/nnet/nnet/nnanalyze:', ... + matlabroot,'/toolbox/nnet/nnet/nncustom:', ... + matlabroot,'/toolbox/nnet/nnet/nndistance:', ... + matlabroot,'/toolbox/nnet/nnet/nnformat:', ... + matlabroot,'/toolbox/nnet/nnet/nninit:', ... + matlabroot,'/toolbox/nnet/nnet/nnlearn:', ... + matlabroot,'/toolbox/nnet/nnet/nnnetinput:', ... + matlabroot,'/toolbox/nnet/nnet/nnnetwork:', ... + matlabroot,'/toolbox/nnet/nnet/nnperformance:', ... + matlabroot,'/toolbox/nnet/nnet/nnplot:', ... + matlabroot,'/toolbox/nnet/nnet/nnprocess:', ... + matlabroot,'/toolbox/nnet/nnet/nnsearch:', ... + matlabroot,'/toolbox/nnet/nnet/nntopology:', ... + matlabroot,'/toolbox/nnet/nnet/nntrain:', ... + matlabroot,'/toolbox/nnet/nnet/nntransfer:', ... + matlabroot,'/toolbox/nnet/nnet/nnweight:', ... + matlabroot,'/toolbox/nnet/nnguis:', ... + matlabroot,'/toolbox/nnet/nnguis/nftool:', ... + matlabroot,'/toolbox/nnet/nnguis/nntool:', ... + matlabroot,'/toolbox/nnet/nnobsolete:', ... + matlabroot,'/toolbox/nnet/nnresource:', ... + matlabroot,'/toolbox/nnet/nnutils:', ... + matlabroot,'/toolbox/optim:', ... + matlabroot,'/toolbox/pde:', ... + matlabroot,'/toolbox/robust/robust:', ... + matlabroot,'/toolbox/robust/rctlmi:', ... + matlabroot,'/toolbox/robust/rctutil:', ... + matlabroot,'/toolbox/robust/rctdemos:', ... + matlabroot,'/toolbox/robust/rctobsolete/robust:', ... + matlabroot,'/toolbox/robust/rctobsolete/lmi:', ... + matlabroot,'/toolbox/robust/rctobsolete/mutools/commands:', ... + matlabroot,'/toolbox/robust/rctobsolete/mutools/subs:', ... + matlabroot,'/toolbox/signal/signal:', ... + matlabroot,'/toolbox/signal/sigtools:', ... + matlabroot,'/toolbox/signal/sptoolgui:', ... + matlabroot,'/toolbox/signal/sigdemos:', ... + matlabroot,'/toolbox/shared/spcuilib:', ... + matlabroot,'/toolbox/slcontrol/slcontrol:', ... + matlabroot,'/toolbox/slcontrol/slctrlguis:', ... + matlabroot,'/toolbox/slcontrol/slctrlutil:', ... + matlabroot,'/toolbox/slcontrol/slctrldemos:', ... + matlabroot,'/toolbox/splines:', ... + matlabroot,'/toolbox/stats:', ... + matlabroot,'/toolbox/vipblks/vipblks:', ... + matlabroot,'/toolbox/vipblks/vipmasks:', ... + matlabroot,'/toolbox/vipblks/vipmex:', ... + matlabroot,'/toolbox/vipblks/vipdemos:', ... + matlabroot,'/toolbox/wavelet/wavelet:', ... + matlabroot,'/toolbox/wavelet/wmultisig1d:', ... + matlabroot,'/toolbox/wavelet/wavedemo:', ... + matlabroot,'/work:', ... +%%% END ENTRIES %%% + ... +]; + +p = [userpath,p]; diff --git a/SD-VBS/common/toolbox/lagrcv/test/test_lk.m b/SD-VBS/common/toolbox/lagrcv/test/test_lk.m new file mode 100755 index 0000000..c6ca701 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/test/test_lk.m @@ -0,0 +1,74 @@ +addpath ~/Matlab/Toolbox/lagrcv/ +addpath ~/Matlab/Toolbox/toolbox_basic/filter +addpath ~/Matlab/Toolbox/ikkjin/ + +N_FEA=1600; +WINSZ=8; %size of sum-up window +NO_PYR=2; +SUPPRESION_RADIUS=10; +LK_ITER=20; +IMAGE_DIR='~/backup/Research/ant/Transport/' +filelist=dir(fullfile(IMAGE_DIR, '*.jpg')); +flen=length(filelist); + +img_idx_cur=[1:flen]; + +%subplot(1,2,1);imshow(Iprev) +%/hold on +%//scatter(features(2,:),features(1,:),'r') +%% +imgName=fullfile(IMAGE_DIR,filelist(img_idx_cur(1)).name); +Icur=imread(imgName); +Icur=rgb2gray(Icur); +Icur=calcImgBlurMex(double(Icur)); +%% + +Jpyr=getPyramid(Icur, 2); + +[lambda tr det c_xx c_xy c_yy] =calcTextureMex(double(Icur), WINSZ); +imgsz=size(lambda); +lambda([1:8 end-8:end],:)=0; +lambda(:,[1:8 end-8:end])=0; +[temp idx]=sort(lambda(:), 'descend'); + +%% +featureIdx=idx(1:N_FEA); +features=zeros(3, N_FEA); +features(1,:)=ceil(featureIdx/imgsz(1)); +features(2,:)=featureIdx'-(features(1,:)-1)*imgsz(1); +features(3,:)=lambda(featureIdx); + +imagesc(lambda); hold on +scatter(features(1,:), features(2,:), 'r+');hold off +%% +interestPnt=getANMS(features(1,:)', features(2,:)', features(3,:)', SUPPRESION_RADIUS); +interestPnt=interestPnt'; +scatter(interestPnt(1,:), interestPnt(2,:), 'g+') +%% +features=interestPnt(1:2,:); +%% + +for iter=img_idx_cur + Iprev=Icur; + Icur=imread(fullfile(IMAGE_DIR,filelist(img_idx_cur(iter)).name)); + Icur=rgb2gray(Icur); + Icur=calcImgBlurMex(double(Icur)); + + Ipyr=Jpyr; + Jpyr=getPyramid(Icur, 2); + + [dxPyr dyPyr]=calcSobelPyrMex(Ipyr,2); + + [lambda tr det c_xx c_xy c_yy] = calcTexturePyrMex(dxPyr, dyPyr, WINSZ, NO_PYR); + + [newpoints status]=calcOptFlowLKPyrMex(Ipyr, dxPyr, dyPyr, Jpyr, double(features), 4, 0.03, LK_ITER, c_xx, c_xy, c_yy); + + newpoints=newpoints(:,find(status)); + figure(1); + imagesc(Icur);colormap gray + hold on;scatter(newpoints(1,:), newpoints(2,:), 'r+'); hold off; + drawnow + %print('-djpeg', sprintf('result/result_%03d', iter)) + %pause + features=newpoints; +end diff --git a/SD-VBS/common/toolbox/lagrcv/test/test_lk_disp.m b/SD-VBS/common/toolbox/lagrcv/test/test_lk_disp.m new file mode 100755 index 0000000..2d55fe3 --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/test/test_lk_disp.m @@ -0,0 +1,49 @@ +addpath /u/ikkjin/Matlab/Toolbox/lagrcv + +%Iprev=imread('/Projects/LAGR/logs/Test5/left_APIrun530-1/l24Aug05-abacination-1124876208.403311.ppm'); +%Icur=imread('/Projects/LAGR/logs/Test5/left_APIrun530-1/l24Aug05-abacination-1124876208.670013.ppm'); +Iprev=imread('img0.ppm'); +Icur=imread('img1.ppm'); + +Iprev=rgb2gray(Iprev); +Icur=rgb2gray(Icur); + +tic + [ features numvalid ] = goodFeaturesToTrack(Iprev, 0.3, 10); +toc +subplot(1,2,1);imshow(Iprev) +hold on +scatter(features(2,:),features(1,:),'r') + +Ipyr=getPyramid(Iprev, 3); +Jpyr=getPyramid(Icur, 3); +tic +[dxPyr2 dyPyr2]=calcGradientPyrMex(Ipyr,3); +toc +tic +[dxPyr dyPyr]=calcSobelPyrMex(Ipyr,3); +toc + +features=features(:,1:211); +for i=20 + features2=[features(2,:); features(1,:)]; +tic + [ newpoints status pyr1 ] = calcOpticalFlowPyrLK(Iprev,Icur,features, i); +toc +tic + [newpoints2 status]=calcOptFlowLKPyrMex(Ipyr, dxPyr, dyPyr, Jpyr, double(features2), 4, 0.03, i); +toc + newpoints2=[newpoints2(2,:); newpoints2(1,:)]; +features_out=features(:,find(status)); +newpoints=newpoints(:,find(status)); +newpoints2=newpoints2(:,find(status)); +subplot(1,2,1);imshow(Iprev);hold on +quiver(features_out(2,:),features_out(1,:), newpoints(2,:)-features_out(2,:), newpoints(1,:)-features_out(1,:),0,'r');hold off +subplot(1,2,2);imshow(Iprev);hold on +quiver(features_out(2,:),features_out(1,:), newpoints2(2,:)-features_out(2,:), newpoints2(1,:)-features_out(1,:),0,'r');hold off +%subplot(1,2,2);imshow(Icur);hold on +%scatter(newpoints2(2,:),newpoints2(1,:),'r');hold off +%sum(sum((newpoints-newpoints2).^2)) + +pause +end diff --git a/SD-VBS/common/toolbox/lagrcv/test/test_lk_opencv.m b/SD-VBS/common/toolbox/lagrcv/test/test_lk_opencv.m new file mode 100755 index 0000000..9cfd1ae --- /dev/null +++ b/SD-VBS/common/toolbox/lagrcv/test/test_lk_opencv.m @@ -0,0 +1,47 @@ +addpath ~/Matlab/Toolbox/lagrcv/ +addpath ~/Matlab/Toolbox/toolbox_basic/filter +addpath ~/Matlab/Toolbox/ikkjin/ + +IMAGE_DIR='/data/insecure/images/ants/Transport/' +filelist=dir(fullfile(IMAGE_DIR, '*.jpg')); +flen=length(filelist); + +img_idx_cur=[1:flen]; + +%subplot(1,2,1);imshow(Iprev) +%/hold on +%//scatter(features(2,:),features(1,:),'r') +%Iprev=imread(fullfile(IMAGE_DIR,filelist(img_idx_prev(1)).name)); +%% +imgName=fullfile(IMAGE_DIR,filelist(img_idx_cur(1)).name); +Icur=imread(imgName); +%% +Icur=smooth(double(rgb2gray(Icur)), 4); +%% +Icur=Icur(1:2:end,1:2:end); + +[ features numvalid ] = goodFeaturesToTrack(Icur, 0.3, 10); +features=features(:,1:numvalid); + +figure(1); +imagesc(Icur);colormap gray +hold on;scatter(features(2,:), features(1,:), 'r+'); hold off; + +%% +for iter=img_idx_cur + Iprev=Icur; + Icur=imread(fullfile(IMAGE_DIR,filelist(img_idx_cur(iter)).name)); + Icur=calcImgBlurMex(rgb2gray(Icur)); + + tic + [ newpoints status pyr1 ] = calcOpticalFlowPyrLK(Iprev,Icur,features); + toc + newpoints=newpoints(:,find(status)); + figure(1); + imagesc(Icur);colormap gray + hold on;scatter(newpoints(2,:), newpoints(1,:), 'r+'); hold off; + drawnow + print('-djpeg', sprintf('result/result_%03d', iter)) + %pause + features=newpoints; +end diff --git a/SD-VBS/common/toolbox/mex_template.c b/SD-VBS/common/toolbox/mex_template.c new file mode 100755 index 0000000..b85683f --- /dev/null +++ b/SD-VBS/common/toolbox/mex_template.c @@ -0,0 +1,58 @@ +//function W = mex_template(X,Y); + +#include +#include +//#include +//#include "mex_util.cpp" + +//#define PI 3.1415927 + +void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray +*in[]) { + //reading in + const mxArray *X = in[0]; + double *pr = mxGetPr(X); + int *ir = mxGetIr(X); + int *jc = mxGetJc(X); + int m = mxGetM(X); + int n = mxGetN(X); + + mxArray *cell_1 = mxGetCell(cell_input,0); + mxGetData() + mxGetNumberOfDimensions() + mxGetDimensions() + mxGetNumberOfElements() + + //sparse array reading + int i,j,k; + double x; + for (j=0;j0.1)&~two_focal, + disp('Should use two focals in x and y...'); +end; + +% Deduce the translation vector: + +Tc_2 = distance * H_2; + + + + + +return; + + V_hori_pix/V_hori_pix(3) + V_vert_pix/V_vert_pix(3) + V_diag1_pix/V_diag1_pix(3) + V_diag2_pix/V_diag2_pix(3) diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/Rectangle2Square.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/Rectangle2Square.m new file mode 100755 index 0000000..a6bbbe5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/Rectangle2Square.m @@ -0,0 +1,19 @@ +function [square] = Rectangle2Square(rectangle,L,W); + +% Generate the square from a rectangle of known segment lengths +% from pt1 to pt2 : L +% from pt2 to pt3 : W + +[u_hori,u_vert] = UnWarpPlane(rectangle); + +coeff_x = sqrt(W/L); +coeff_y = 1/coeff_x; + +x_coord = [ 0 coeff_x coeff_x 0]; +y_coord = [ 0 0 coeff_y coeff_y]; + + +square = rectangle(:,1) * ones(1,4) + u_hori*x_coord + u_vert*y_coord; +square = square ./ (ones(3,1)*square(3,:)); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/UnWarpPlane.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/UnWarpPlane.m new file mode 100755 index 0000000..8addf52 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/UnWarpPlane.m @@ -0,0 +1,54 @@ +function [u_hori,u_vert] = UnWarpPlane(x1,x2,x3,x4); + +% Recovers the two 3D directions of the rectangular patch x1x2x3x4 +% x1 is the origin point, ie any point of planar coordinate (x,y) on the +% rectangular patch will be projected on the image plane at: +% x1 + x * u_hori + y * u_vert +% +% Note: u_hori and u_vert are also the two vanishing points. + + +if nargin < 4, + + x4 = x1(:,4); + x3 = x1(:,3); + x2 = x1(:,2); + x1 = x1(:,1); + +end; + + +% Image Projection: +L1 = cross(x1,x2); +L2 = cross(x4,x3); +L3 = cross(x2,x3); +L4 = cross(x1,x4); + +% Vanishing point: +V1 = cross(L1,L2); +V2 = cross(L3,L4); + +% Horizon line: +H = cross(V1,V2); + +if H(3) < 0, H = -H; end; + + +H = H / norm(H); + + +X1 = x1 / dot(H,x1); +X2 = x2 / dot(H,x2); +X3 = x3 / dot(H,x3); +X4 = x4 / dot(H,x4); + +scale = X1(3); + +X1 = X1/scale; +X2 = X2/scale; +X3 = X3/scale; +X4 = X4/scale; + + +u_hori = X2 - X1; +u_vert = X4 - X1; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/add_suppress.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/add_suppress.m new file mode 100755 index 0000000..a8a32c0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/add_suppress.m @@ -0,0 +1,98 @@ + +if ~exist('n_ima'), + fprintf(1,'No data to process.\n'); + return; +end; + + +check_active_images; + + +fprintf(1,'\nThis function is useful to select a subset of images to calibrate\n'); + + fprintf(1,'\nThere are currently %d active images selected for calibration (out of %d):\n',length(ind_active),n_ima); + + if ~isempty(ind_active), + + for ii = 1:length(ind_active)-2, + + fprintf(1,'%d, ',ind_active(ii)); + + end; + + fprintf(1,'%d and %d.',ind_active(end-1),ind_active(end)); + + end; + + fprintf(1,'\n'); + + +fprintf(1,'\nDo you want to suppress or add images from that list?\n'); + +choice = 2; + +while (choice~=0)&(choice~=1), + choice = input('For suppressing images enter 0, for adding images enter 1 ([]=no change): '); + if isempty(choice), + fprintf(1,'No change applied to the list of active images.\n'); + return; + end; + if (choice~=0)&(choice~=1), + disp('Bad entry. Try again.'); + end; +end; + + +if choice, + + ima_numbers = input('Number(s) of image(s) to add ([] = all images) = '); + +if isempty(ima_numbers), + fprintf(1,'All %d images are now active\n',n_ima); + ima_proc = 1:n_ima; + else + ima_proc = ima_numbers; + end; + +else + + + ima_numbers = input('Number(s) of image(s) to suppress ([] = no image) = '); + + if isempty(ima_numbers), + fprintf(1,'No image has been suppressed. No modication of the list of active images.\n',n_ima); + ima_proc = []; + else + ima_proc = ima_numbers; + end; + +end; + +if ~isempty(ima_proc), + + active_images(ima_proc) = choice * ones(1,length(ima_proc)); + +end; + + + check_active_images; + + + fprintf(1,'\nThere is now a total of %d active images for calibration:\n',length(ind_active)); + + if ~isempty(ind_active), + + for ii = 1:length(ind_active)-2, + + fprintf(1,'%d, ',ind_active(ii)); + + end; + + fprintf(1,'%d and %d.',ind_active(end-1),ind_active(end)); + + end; + + fprintf(1,'\n\nYou may now run ''Calibration'' to recalibrate based on this new set of images.\n'); + + + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/analyse_error.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/analyse_error.m new file mode 100755 index 0000000..7a9cf0f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/analyse_error.m @@ -0,0 +1,141 @@ +% Color code for each image: + +if ~exist('n_ima')|~exist('fc'), + fprintf(1,'No calibration data available.\n'); + return; +end; + +check_active_images; + +if ~exist(['ex_' num2str(ind_active(1)) ]), + fprintf(1,'Need to calibrate before analysing reprojection error. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + + +%if ~exist('no_grid'), + no_grid = 0; +%end; + +colors = 'brgkcm'; + + +figure(5); + +for kk = 1:n_ima, + if exist(['y_' num2str(kk)]), + if active_images(kk) & eval(['~isnan(y_' num2str(kk) '(1,1))']), + + if ~no_grid, + eval(['XX_kk = X_' num2str(kk) ';']); + N_kk = size(XX_kk,2); + + if ~exist(['n_sq_x_' num2str(kk)]), + no_grid = 1; + end; + + if ~no_grid, + eval(['n_sq_x = n_sq_x_' num2str(kk) ';']); + eval(['n_sq_y = n_sq_y_' num2str(kk) ';']); + if (N_kk ~= ((n_sq_x+1)*(n_sq_y+1))), + no_grid = 1; + end; + end; + end; + + eval(['plot(ex_' num2str(kk) '(1,:),ex_' num2str(kk) '(2,:),''' colors(rem(kk-1,6)+1) '+'');']); + + hold on; + end; + end; +end; + +hold off; +axis('equal'); +if 1, %~no_grid, + title('Reprojection error (in pixel) - To exit: right button'); +else + title('Reprojection error (in pixel)'); +end; +xlabel('x'); +ylabel('y'); + +set(5,'Name','error','NumberTitle','off'); + + + +err_std = std(ex')'; + +fprintf(1,'Pixel error: err = [ %3.5f %3.5f] (all active images)\n\n',err_std); + + +b = 1; + +while b==1, + +[xp,yp,b] = ginput3(1); + +if b==1, +ddd = (ex(1,:)-xp).^2 + (ex(2,:)-yp).^2; + +[mind,indmin] = min(ddd); + + +done = 0; +kk_ima = 1; +while (~done)&(kk_ima<=n_ima), + %fprintf(1,'%d...',kk_ima); + eval(['ex_kk = ex_' num2str(kk_ima) ';']); + sol_kk = find((ex_kk(1,:) == ex(1,indmin))&(ex_kk(2,:) == ex(2,indmin))); + if isempty(sol_kk), + kk_ima = kk_ima + 1; + else + done = 1; + end; +end; + +if ~no_grid, + +eval(['n_sq_x = n_sq_x_' num2str(kk_ima) ';']); +eval(['n_sq_y = n_sq_y_' num2str(kk_ima) ';']); + +Nx = n_sq_x+1; +Ny = n_sq_y+1; + +y1 = floor((sol_kk-1)./Nx); +x1 = sol_kk - 1 - Nx*y1; %rem(sol_kk-1,Nx); + +y1 = (n_sq_y+1) - y1; +x1 = x1 + 1; + +fprintf(1,'\nSelected image: %d\nSelected point: (col,row)=(%d,%d)\nNcol=%d, Nrow=%d\n',[kk_ima x1 y1 Nx Ny]); +fprintf(1,'Pixel error = (%3.5f,%3.5f)\n',[ex(1,indmin) ex(2,indmin)]); + +else + + eval(['x_kk = x_' num2str(kk_ima) ';']); + + xpt = x_kk(:,sol_kk); + +fprintf(1,'\nSelected image: %d\nImage coordinates (in pixel): (%3.2f,%3.2f)\n',[kk_ima xpt']); +fprintf(1,'Pixel error = (%3.5f,%3.5f)\n',[ex(1,indmin) ex(2,indmin)]); + + +end; + + +if exist(['wintx_' num2str(kk_ima)]), + + eval(['wintx = wintx_' num2str(kk_ima) ';']); + eval(['winty = winty_' num2str(kk_ima) ';']); + + fprintf(1,'Window size: wintx = %d, winty = %d\n',[wintx winty]); +end; + + +end; + +end; + +disp('done'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/apply_distortion.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/apply_distortion.m new file mode 100755 index 0000000..f5c5b48 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/apply_distortion.m @@ -0,0 +1,137 @@ +function [xd,dxddk] = apply_distortion(x,k) + + +[m,n] = size(x); + +% Add distortion: + +r2 = x(1,:).^2 + x(2,:).^2; + +r4 = r2.^2; + +% Radial distortion: + +cdist = 1 + k(1) * r2 + k(2) * r4; + +if nargout > 1, + dcdistdk = [ r2' r4' zeros(n,2)]; +end; + + +xd1 = x .* (ones(2,1)*cdist); + +coeff = (reshape([cdist;cdist],2*n,1)*ones(1,3)); + +if nargout > 1, + dxd1dk = zeros(2*n,4); + dxd1dk(1:2:end,:) = (x(1,:)'*ones(1,4)) .* dcdistdk; + dxd1dk(2:2:end,:) = (x(2,:)'*ones(1,4)) .* dcdistdk; +end; + + +% tangential distortion: + +a1 = 2.*x(1,:).*x(2,:); +a2 = r2 + 2*x(1,:).^2; +a3 = r2 + 2*x(2,:).^2; + +delta_x = [k(3)*a1 + k(4)*a2 ; + k(3) * a3 + k(4)*a1]; + +aa = (2*k(3)*x(2,:)+6*k(4)*x(1,:))'*ones(1,3); +bb = (2*k(3)*x(1,:)+2*k(4)*x(2,:))'*ones(1,3); +cc = (6*k(3)*x(2,:)+2*k(4)*x(1,:))'*ones(1,3); + +if nargout > 1, + ddelta_xdk = zeros(2*n,4); + ddelta_xdk(1:2:end,3) = a1'; + ddelta_xdk(1:2:end,4) = a2'; + ddelta_xdk(2:2:end,3) = a3'; + ddelta_xdk(2:2:end,4) = a1'; +end; + +xd = xd1 + delta_x; + +if nargout > 1, + dxddk = dxd1dk + ddelta_xdk ; +end; + + +return; + +% Test of the Jacobians: + +n = 10; + +X = 10*randn(3,n); +om = randn(3,1); +T = [10*randn(2,1);40]; +f = 1000*rand(2,1); +c = 1000*randn(2,1); +k = 0.5*randn(4,1); + + +[x,dxdom,dxdT,dxdf,dxdc,dxdk] = project_points(X,om,T,f,c,k); + + +% Test on om: NOT OK + +dom = 0.000000001 * norm(om)*randn(3,1); +om2 = om + dom; + +[x2] = project_points(X,om2,T,f,c,k); + +x_pred = x + reshape(dxdom * dom,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on T: OK!! + +dT = 0.0001 * norm(T)*randn(3,1); +T2 = T + dT; + +[x2] = project_points(X,om,T2,f,c,k); + +x_pred = x + reshape(dxdT * dT,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + + +% Test on f: OK!! + +df = 0.001 * norm(f)*randn(2,1); +f2 = f + df; + +[x2] = project_points(X,om,T,f2,c,k); + +x_pred = x + reshape(dxdf * df,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on c: OK!! + +dc = 0.01 * norm(c)*randn(2,1); +c2 = c + dc; + +[x2] = project_points(X,om,T,f,c2,k); + +x_pred = x + reshape(dxdc * dc,2,n); + +norm(x2-x)/norm(x2 - x_pred) + +% Test on k: OK!! + +dk = 0.001 * norm(4)*randn(4,1); +k2 = k + dk; + +[x2] = project_points(X,om,T,f,c,k2); + +x_pred = x + reshape(dxdk * dk,2,n); + +norm(x2-x)/norm(x2 - x_pred) diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/calib_gui.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/calib_gui.m new file mode 100755 index 0000000..d591d03 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/calib_gui.m @@ -0,0 +1,117 @@ +fig_number = 1; + +n_row = 4; +n_col = 4; + +string_list = cell(n_row,n_col); +callback_list = cell(n_row,n_col); + +x_size = 85; +y_size = 14; +gap_x = 0; +font_name = 'clean'; +font_size = 8; + +title_figure = 'Camera Calibration Toolbox'; + +string_list{1,1} = 'Image names'; +string_list{1,2} = 'Read images'; +string_list{1,3} = 'Extract grid corners'; +string_list{1,4} = 'Calibration'; +string_list{2,1} = 'Show Extrinsic'; +string_list{2,2} = 'Reproject on images'; +string_list{2,3} = 'Analyse error'; +string_list{2,4} = 'Recomp. corners'; +string_list{3,1} = 'Add/Suppress images'; +string_list{3,2} = 'Save'; +string_list{3,3} = 'Load'; +string_list{3,4} = 'Exit'; + +string_list{4,1} = 'Comp. Extrinsic'; +string_list{4,2} = 'Undistort image'; +string_list{4,3} = 'Export calib data'; + + +callback_list{1,1} = 'data_calib;'; +callback_list{1,2} = 'ima_read_calib;'; +callback_list{1,3} = 'click_calib;'; +callback_list{1,4} = 'go_calib_optim;'; +callback_list{2,1} = 'ext_calib;'; +callback_list{2,2} = 'reproject_calib;'; +callback_list{2,3} = 'analyse_error;'; +callback_list{2,4} = 'recomp_corner_calib;'; +callback_list{3,1} = 'add_suppress;'; +callback_list{3,2} = 'saving_calib;'; +callback_list{3,3} = 'loading_calib;'; +callback_list{3,4} = ['disp(''Bye. To run again, type calib_gui.''); close(' num2str(fig_number) ');']; + +callback_list{4,1} = 'extrinsic_computation;'; +callback_list{4,2} = 'undistort_image;'; +callback_list{4,3} = 'export_calib_data;'; + + +%------- BEGIN PROECTED REGION -----------% +%------- DO NOT EDIT IN THIS REGION -----------% + +figure(fig_number); clf; +pos = get(fig_number,'Position'); + +fig_size_x = x_size*n_col+(n_col+1)*gap_x; +fig_size_y = y_size*n_row+(n_row+1)*gap_x; + +set(fig_number,'Units','points', ... + 'BackingStore','off', ... + 'Color',[0.8 0.8 0.8], ... + 'MenuBar','none', ... + 'Resize','off', ... + 'Name',title_figure, ... +'Position',[pos(1) pos(2) fig_size_x fig_size_y], ... +'NumberTitle','off'); %,'WindowButtonMotionFcn',['figure(' num2str(fig_number) ');']); + +h_mat = zeros(n_row,n_col); + +posx = zeros(n_row,n_col); +posy = zeros(n_row,n_col); + +for i=n_row:-1:1, + for j = n_col:-1:1, + posx(i,j) = gap_x+(j-1)*(x_size+gap_x); + posy(i,j) = fig_size_y - i*(gap_x+y_size); + end; +end; + +for i=n_row:-1:1, + for j = n_col:-1:1, + if ~isempty(string_list{i,j}) & ~isempty(callback_list{i,j}), + h_mat(i,j) = uicontrol('Parent',fig_number, ... + 'Units','points', ... + 'Callback',callback_list{i,j}, ... + 'ListboxTop',0, ... + 'Position',[posx(i,j) posy(i,j) x_size y_size], ... + 'String',string_list{i,j}, ... + 'fontsize',font_size,... + 'fontname',font_name,... + 'Tag','Pushbutton1'); + end; + end; +end; + +%------ END PROTECTED REGION ----------------% + +if 0, +%-- VERSION: + +uicontrol('Parent',fig_number, ... + 'Units','points', ... + 'ListboxTop',0, ... + 'Position',[(fig_size_x-x_size/2)-2 -5 x_size/2 y_size], ... + 'String','ver. 1.0', ... + 'fontsize',8,... + 'BackgroundColor',[0.8 0.8 0.8], ... + 'fontname','clean',... + 'HorizontalAlignment','right', ... + 'Style','text'); +end; + + +%clear callback_list string_list fig_number fig_size_x fig_size_y i j n_col n_row pos string_list title_figure x_size y_size font_name font_size gap_x h_mat posx posy diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_active_images.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_active_images.m new file mode 100755 index 0000000..fc365a5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_active_images.m @@ -0,0 +1,19 @@ + +if ~exist('active_images'), + active_images = ones(1,n_ima); +end; +n_act = length(active_images); +if n_act < n_ima, + active_images = [active_images ones(1,n_ima-n_act)]; +else + if n_act > n_ima, + active_images = active_images(1:n_ima); + end; +end; + +ind_active = find(active_images); + +if prod(active_images == 0), + disp('Error: There is no active image'); + break +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_convergence.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_convergence.m new file mode 100755 index 0000000..c4b13fd --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_convergence.m @@ -0,0 +1,48 @@ +%%% Replay the set of solution vectors: + + +if ~exist('param_list'), + if ~exist('solution'); + fprintf(1,'Error: Need to calibrate first\n'); + return; + else + param_list = solution; + end; +end; + +N_iter = size(param_list,2); + +if N_iter == 1, + fprintf(1,'Warning: There is a unique state in the list of parameters.\n'); +end; + + + +%M = moviein(N_iter); + +for nn = 1:N_iter, + + solution = param_list(:,nn); + + extract_parameters; + comp_error_calib; + + ext_calib; + + drawnow; + +% Mnn = getframe(gcf); + +% M(:,nn) = Mnn; + +end; + +%fig = gcf; + + +%figure(fig+1); +%close; +%figure(fig+1); + +%clf; +%movie(M,20); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_directory.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_directory.m new file mode 100755 index 0000000..dc23149 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_directory.m @@ -0,0 +1,97 @@ +% This small script looks in the direcory and checks if the images are there. +% +% This works only on Matlab 5.x (otherwise, the dir commands works differently) + +% (c) Jean-Yves Bouguet - Dec. 27th, 1999 + +l = dir([calib_name '*']); + +Nl = size(l,1); +Nima_valid = 0; +ind_valid = []; +loc_extension = []; +length_name = size(calib_name,2); + +if Nl > 0, + + for pp = 1:Nl, + filenamepp = l(pp).name; + iii = findstr(filenamepp,calib_name); + + loc_ext = findstr(filenamepp,format_image); + string_num = filenamepp(length_name+1:loc_ext - 2); + + if isempty(str2num(string_num)), + iii = []; + end; + + + if ~isempty(iii), + if (iii(1) ~= 1), + iii = []; + end; + end; + + + + if ~isempty(iii) & ~isempty(loc_ext), + + Nima_valid = Nima_valid + 1; + ind_valid = [ind_valid pp]; + loc_extension = [loc_extension loc_ext(1)]; + + end; + + end; + + if (Nima_valid==0), + + fprintf(1,'No image found. File format may be wrong.\n'); + + else + + % Get all the string numbers: + + string_length = zeros(1,Nima_valid); + indices = zeros(1,Nima_valid); + + + for ppp = 1:Nima_valid, + + name = l(ind_valid(ppp)).name; + string_num = name(length_name+1:loc_extension(ppp) - 2); + string_length(ppp) = size(string_num,2); + indices(ppp) = str2num(string_num); + + end; + + % Number of images... + first_num = min(indices); + n_ima = max(indices) - first_num + 1; + + if min(string_length) == max(string_length), + + N_slots = min(string_length); + type_numbering = 1; + + else + + N_slots = 1; + type_numbering = 0; + + end; + + image_numbers = first_num:n_ima-1+first_num; + + %%% By default, all the images are active for calibration: + + active_images = ones(1,n_ima); + + end; + +else + + fprintf(1,'No image found. Basename may be wrong.\n'); + +end; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_extracted_images.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_extracted_images.m new file mode 100755 index 0000000..fa7df87 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/check_extracted_images.m @@ -0,0 +1,37 @@ +check_active_images; + +for kk = ind_active, + + if ~exist(['x_' num2str(kk)]), + + fprintf(1,'WARNING: Need to extract grid corners on image %d\n',kk); + + active_images(kk) = 0; + + eval(['dX_' num2str(kk) ' = NaN;']); + eval(['dY_' num2str(kk) ' = NaN;']); + + eval(['wintx_' num2str(kk) ' = NaN;']); + eval(['winty_' num2str(kk) ' = NaN;']); + + eval(['x_' num2str(kk) ' = NaN*ones(2,1);']); + eval(['X_' num2str(kk) ' = NaN*ones(3,1);']); + + eval(['n_sq_x_' num2str(kk) ' = NaN;']); + eval(['n_sq_y_' num2str(kk) ' = NaN;']); + + else + + eval(['xkk = x_' num2str(kk) ';']); + + if isnan(xkk(1)), + + fprintf(1,'WARNING: Need to extract grid corners on image %d - This image is now set inactive\n',kk); + + active_images(kk) = 0; + + end; + + end; + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clear_windows.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clear_windows.m new file mode 100755 index 0000000..1eccbd3 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clear_windows.m @@ -0,0 +1,4 @@ +for kk = 1:n_ima, + eval(['clear wintx_' num2str(kk)]); + eval(['clear winty_' num2str(kk)]); +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clearwin.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clearwin.m new file mode 100755 index 0000000..a04be67 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/clearwin.m @@ -0,0 +1,10 @@ +% Function that clears all the wintx_i and winty_i +% In normal operation of the toolbox, this function should not be +% useful. +% only in cases where you want to re-extract corners using the Extract grid corners another time... not common. You might as well use the Recomp. corners. + +for kk = 1:n_ima, + + eval(['clear wintx_' num2str(kk) ' winty_' num2str(kk)]); + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_calib.m new file mode 100755 index 0000000..1a6d2d7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_calib.m @@ -0,0 +1,193 @@ +%if exist('images_read'); +% active_images = active_images & images_read; +%end; + +var2fix = 'dX_default'; + +fixvariable; + +var2fix = 'dY_default'; + +fixvariable; + +var2fix = 'map'; + +fixvariable; + + +if ~exist('n_ima'), + data_calib; +end; + +check_active_images; + +if ~exist(['I_' num2str(ind_active(1))]), + ima_read_calib; + if isempty(ind_read), + disp('Cannot extract corners without images'); + return; + end; +end; + + +%wintx = 10; % neigborhood of integration for +%winty = 10; % the corner finder + +fprintf(1,'\nExtraction of the grid corners on the images\n'); + + +if ~exist('map'), map = gray(256); end; + + +disp('WARNING!!! Do not forget to change dX_default and dY_default in click_calib.m!!!') + +if ~exist('dX_default'); + +% Default size of the pattern squares; + +% Setup of JY (old at Caltech) +%dX_default = 21.9250/11; +%dY_default = 18.1250/9; + +% Setup of JY (new at Intel) +%dX_default = 1.9750; +%dY_default = 1.9865; + + +% Setup of Luis and Enrico +%dX_default = 67.7/16; +%dY_default = 50.65/12; + + +% Setup of German +%dX_default = 10.16; +%dY_default = 10.16; + +% Setup of JY (new at Intel) +%dX_default = 1.9750*2.54; +%dY_default = 1.9865*2.54; + +% Setup of JY - 3D calibration rig at Intel (new at Intel) +%dX_default = 3; +%dY_default = 3; + +% Setup of JY - 3D calibration rig at Intel (new at Intel) - use units in mm to match Zhang +dX_default = 30; +dY_default = 30; + +end; + + +if ~exist('dont_ask'), + dont_ask = 0; +end; + + +if ~dont_ask, + ima_numbers = input('Number(s) of image(s) to process ([] = all images) = '); +else + ima_numbers = []; +end; + +if isempty(ima_numbers), + ima_proc = 1:n_ima; +else + ima_proc = ima_numbers; +end; + + +% Useful option to add images: +kk_first = ima_proc(1); %input('Start image number ([]=1=first): '); + +%if isempty(kk_first), kk_first = 1; end; + + +if exist(['wintx_' num2str(kk_first)]), + + eval(['wintxkk = wintx_' num2str(kk_first) ';']); + + if isempty(wintxkk) | isnan(wintxkk), + + disp('Window size for corner finder (wintx and winty):'); + wintx = input('wintx ([] = 5) = '); + if isempty(wintx), wintx = 5; end; + wintx = round(wintx); + winty = input('winty ([] = 5) = '); + if isempty(winty), winty = 5; end; + winty = round(winty); + + fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + + end; + +else + + disp('Window size for corner finder (wintx and winty):'); + wintx = input('wintx ([] = 5) = '); + if isempty(wintx), wintx = 5; end; + wintx = round(wintx); + winty = input('winty ([] = 5) = '); + if isempty(winty), winty = 5; end; + winty = round(winty); + + fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + +end; + + +for kk = ima_proc, + if exist(['I_' num2str(kk)]), + click_ima_calib; + active_images(kk) = 1; + else + eval(['dX_' num2str(kk) ' = NaN;']); + eval(['dY_' num2str(kk) ' = NaN;']); + + eval(['wintx_' num2str(kk) ' = NaN;']); + eval(['winty_' num2str(kk) ' = NaN;']); + + eval(['x_' num2str(kk) ' = NaN*ones(2,1);']); + eval(['X_' num2str(kk) ' = NaN*ones(3,1);']); + + eval(['n_sq_x_' num2str(kk) ' = NaN;']); + eval(['n_sq_y_' num2str(kk) ' = NaN;']); + end; +end; + + +check_active_images; + + +% Fix potential non-existing variables: + +for kk = 1:n_ima, + if ~exist(['x_' num2str(kk)]), + eval(['dX_' num2str(kk) ' = NaN;']); + eval(['dY_' num2str(kk) ' = NaN;']); + + eval(['wintx_' num2str(kk) ' = NaN;']); + eval(['winty_' num2str(kk) ' = NaN;']); + + eval(['x_' num2str(kk) ' = NaN*ones(2,1);']); + eval(['X_' num2str(kk) ' = NaN*ones(3,1);']); + + eval(['n_sq_x_' num2str(kk) ' = NaN;']); + eval(['n_sq_y_' num2str(kk) ' = NaN;']); + end; +end; + + +string_save = 'save calib_data active_images ind_active wintx winty n_ima type_numbering N_slots first_num image_numbers format_image calib_name Hcal Wcal nx ny map dX_default dY_default dX dY'; + +for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' n_sq_x_' num2str(kk) ' n_sq_y_' num2str(kk) ' wintx_' num2str(kk) ' winty_' num2str(kk) ' dX_' num2str(kk) ' dY_' num2str(kk)]; +end; + +eval(string_save); + +disp('done'); + +return; + +go_calib_optim; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib.m new file mode 100755 index 0000000..f0fd4ca --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib.m @@ -0,0 +1,230 @@ + % Cleaned-up version of init_calib.m + + fprintf(1,'\nProcessing image %d...\n',kk); + + eval(['I = I_' num2str(kk) ';']); + + if exist(['wintx_' num2str(kk)]), + + eval(['wintxkk = wintx_' num2str(kk) ';']); + + if ~isempty(wintxkk) & ~isnan(wintxkk), + + eval(['wintx = wintx_' num2str(kk) ';']); + eval(['winty = winty_' num2str(kk) ';']); + + end; + end; + + + fprintf(1,'Using (wintx,winty)=(%d,%d) - Window size = %dx%d\n',wintx,winty,2*wintx+1,2*winty+1); + + + figure(2); + image(I); + colormap(map); + + title(['Click on the four extreme corners of the rectangular pattern... Image ' num2str(kk)]); + + disp('Click on the four extreme corners of the rectangular complete pattern...'); + + [x,y] = ginput3(4); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + if (x(2) > x(1)), + x4 = x(1);y4 = y(1); x3 = x(2); y3 = y(2); + else + x4 = x(2);y4 = y(2); x3 = x(1); y3 = y(1); + end; + if (x(3) > x(4)), + x2 = x(3);y2 = y(3); x1 = x(4); y1 = y(4); + else + x2 = x(4);y2 = y(4); x1 = x(3); y1 = y(3); + end; + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'g-'); + plot(x,y,'og'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','g','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','g','Fontsize',14); + hold off; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=' num2str(dX_default) 'mm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=' num2str(dY_default) 'mm) = ']); + if isempty(dX), dX = dX_default; else dX_default = dX; end; + if isempty(dY), dY = dY_default; else dY_default = dY; end; + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planar collineation: (return the normalization matrix as well) + + [Homo,Hnorm,inv_Hnorm] = compute_homography ([a00 a10 a11 a01],[0 1 1 0;0 0 1 1;1 1 1 1]); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + + + + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + + + %save all_corners x y grid_pts + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + image(I); colormap(map); hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + + + % Saves all the data into variables: + + eval(['dX_' num2str(kk) ' = dX;']); + eval(['dY_' num2str(kk) ' = dY;']); + + eval(['wintx_' num2str(kk) ' = wintx;']); + eval(['winty_' num2str(kk) ' = winty;']); + + eval(['x_' num2str(kk) ' = x;']); + eval(['X_' num2str(kk) ' = X;']); + + eval(['n_sq_x_' num2str(kk) ' = n_sq_x;']); + eval(['n_sq_y_' num2str(kk) ' = n_sq_y;']); + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib3D.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib3D.m new file mode 100755 index 0000000..7718268 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/click_ima_calib3D.m @@ -0,0 +1,482 @@ + % Cleaned-up version of init_calib.m + + eval(['I = I_' num2str(kk) ';']); + + figure(2); + image(I); + colormap(map); + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%% LEFT PATTERN ACQUISITION %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + title(['Click on the four extreme corners of the left rectangular pattern... Image ' num2str(kk)]); + + disp('Click on the four extreme corners of the left rectangular pattern...'); + + [x,y] = ginput3(4); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + if (x(2) > x(1)), + x4 = x(1);y4 = y(1); x3 = x(2); y3 = y(2); + else + x4 = x(2);y4 = y(2); x3 = x(1); y3 = y(1); + end; + if (x(3) > x(4)), + x2 = x(3);y2 = y(3); x1 = x(4); y1 = y(4); + else + x2 = x(4);y2 = y(4); x1 = x(3); y1 = y(3); + end; + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'g-'); + plot(x,y,'og'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','g','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','g','Fontsize',14); + hold off; + + drawnow; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + + if 1, + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=' num2str(dX_default) 'cm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=' num2str(dY_default) 'cm) = ']); + if isempty(dX), dX = dX_default; else dX_default = dX; end; + if isempty(dY), dY = dY_default; else dY_default = dY; end; + + else + + dX = 3; + dY = 3; + + end; + + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planart collineation: (return the normalization matrice as well) + + [Homo,Hnorm,inv_Hnorm] = compute_collineation (a00, a10, a11, a01); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + if 1, + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + end; + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + %save all_corners x y grid_pts + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + + % Global Homography from plane to pixel coordinates: + + H_total = [1 0 -1 ; 0 1 -1 ; 0 0 1]*Homo*[1 0 0;0 -1 1;0 0 1]*[1/W 0 0 ; 0 1/L 0; 0 0 1]; + % WARNING!!! the first matrix (on the left side) takes care of the transformation of the pixel cooredinates by -1 (previous line) + % If it is not done, then this matrix should not appear (in C) + H_total = H_total / H_total(3,3); + + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + image(I); colormap(map); hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + + + % The left pannel info: + + xl = x; + Xl = X; + nl_sq_x = n_sq_x; + nl_sq_y = n_sq_y; + Hl = H_total; + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%% RIGHT PATTERN ACQUISITION %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + x1 = a10(1)/a10(3); + x4 = a11(1)/a11(3); + + y1 = a10(2)/a10(3); + y4 = a11(2)/a11(3); + + + figure(2); + hold on; + plot([x1 x4],[y1 y4],'c-'); + plot([x1 x4],[y1 y4],'co'); + hold off; + + title(['Click on the two remaining extreme corners of the right rectangular pattern... Image ' num2str(kk)]); + + disp('Click on the two remaining extreme corners of the right rectangular pattern...'); + + [x,y] = ginput3(2); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + x2 = x(2); + x3 = x(1); + + y2 = y(2); + y3 = y(1); + + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'c-'); + plot(x,y,'oc'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','c','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','c','Fontsize',14); + hold off; + drawnow; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + + if 1, + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=' num2str(dX_default) 'cm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=' num2str(dY_default) 'cm) = ']); + if isempty(dX), dX = dX_default; else dX_default = dX; end; + if isempty(dY), dY = dY_default; else dY_default = dY; end; + + else + + dX = 3; + dY = 3; + + end; + + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planart collineation: (return the normalization matrice as well) + + [Homo,Hnorm,inv_Hnorm] = compute_collineation (a00, a10, a11, a01); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + if 1, + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + end; + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + %save all_corners x y grid_pts + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + + % Global Homography from plane to pixel coordinates: + + H_total = [1 0 -1 ; 0 1 -1 ; 0 0 1]*Homo*[1 0 0;0 -1 1;0 0 1]*[1/W 0 0 ; 0 1/L 0; 0 0 1]; + % WARNING!!! the first matrix (on the left side) takes care of the transformation of the pixel cooredinates by -1 (previous line) + % If it is not done, then this matrix should not appear (in C) + H_total = H_total / H_total(3,3); + + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + + + % The right pannel info: + + xr = x; + Xr = X; + nr_sq_x = n_sq_x; + nr_sq_y = n_sq_y; + Hr = H_total; + + + +%%%%%%%% REGROUP THE LEFT AND RIHT PATTERNS %%%%%%%%%%%%% + + +Xr2 = [0 0 1;0 1 0;-1 0 0]*Xr + [dX*nl_sq_x;0;0]*ones(1,length(Xr)); + + +x = [xl xr]; + +X = [Xl Xr2]; + + + + eval(['x_' num2str(kk) ' = x;']); + eval(['X_' num2str(kk) ' = X;']); + + eval(['nl_sq_x_' num2str(kk) ' = nl_sq_x;']); + eval(['nl_sq_y_' num2str(kk) ' = nl_sq_y;']); + + eval(['nr_sq_x_' num2str(kk) ' = nr_sq_x;']); + eval(['nr_sq_y_' num2str(kk) ' = nr_sq_y;']); + + % Save the global planar homography: + + eval(['Hl_' num2str(kk) ' = Hl;']); + eval(['Hr_' num2str(kk) ' = Hr;']); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion.m new file mode 100755 index 0000000..a0f03de --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion.m @@ -0,0 +1,38 @@ +function [x_comp] = comp_distortion(x_dist,k2); + +% [x_comp] = comp_distortion(x_dist,k2); +% +% compensates the radial distortion of the camera +% on the image plane. +% +% x_dist : the image points got without considering the +% radial distortion. +% x : The image plane points after correction for the distortion +% +% x and x_dist are 2xN arrays +% +% NOTE : This compensation has to be done after the substraction +% of the center of projection, and division by the focal +% length. +% +% (do it up to a second order approximation) + +[two,N] = size(x_dist); + +if (two ~= 2 ), + error('ERROR : The dimension of the points should be 2xN'); +end; + +if length(k2) > 2, + [x_comp] = comp_distortion_oulu(x_dist,k2); +else + +radius_2= x_dist(1,:).^2 + x_dist(2,:).^2; +radial_distortion = 1 + ones(2,1)*(k2 * radius_2); +radius_2_comp = (x_dist(1,:).^2 + x_dist(2,:).^2) ./ radial_distortion(1,:); +radial_distortion = 1 + ones(2,1)*(k2 * radius_2_comp); +x_comp = x_dist ./ radial_distortion; + +end; + +%% Function completely checked : It works fine !!! \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion2.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion2.m new file mode 100755 index 0000000..532ee9a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion2.m @@ -0,0 +1,71 @@ +function [x_comp] = comp_distortion(x_dist,k2); + +% [x_comp] = comp_distortion(x_dist,k2); +% +% compensates the radial distortion of the camera +% on the image plane. +% +% x_dist : the image points got without considering the +% radial distortion. +% k2: Radial distortion factor +% +% x : The image plane points after correction for the distortion +% +% x and x_dist are 2xN arrays +% +% NOTE : This compensation has to be done after the substraction +% of the center of projection, and division by the focal +% length. +% +% Solve for cubic roots using method from Numerical Recipes in C 2nd Ed. +% pages 184-185. + + +% California Institute of Technology +% (c) Jean-Yves Bouguet - April 27th, 1998 + +% fully checked! JYB, april 27th, 1998 - 2am + +if k2 ~= 0, + +[two,N] = size(x_dist); + +if (two ~= 2 ), + error('ERROR : The dimension of the points should be 2xN'); +end; + + +ph = atan2(x_dist(2,:),x_dist(1,:)); + +Q = -1/(3*k2); +R = -x_dist(1,:)./(2*k2*cos(ph)); + +R2 = R.^2; +Q3 = Q^3; + + +if k2 < 0, + + % this works in all practical situations (it starts failing for very large + % values of k2) + + th = acos(R./sqrt(Q3)); + r = -2*sqrt(Q)*cos((th-2*pi)/3); + +else + + % note: this always works, even for ridiculous values of k2 + + A = (sqrt(R2-Q3)-R).^(1/3); + B = Q*(1./A); + r = (A+B); + +end; + +x_comp = [r.*cos(ph); r.*sin(ph)]; + +else + + x_comp = x_dist; + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion_oulu.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion_oulu.m new file mode 100755 index 0000000..67d02d5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_distortion_oulu.m @@ -0,0 +1,47 @@ +function [x] = comp_distortion_oulu(xd,k); + +%comp_distortion_oulu.m +% +%[x] = comp_distortion_oulu(xd,k) +% +%Compensates for radial and tangential distortion. Model From Oulu university. +%For more informatino about the distortion model, check the forward projection mapping function: +%project_points.m +% +%INPUT: xd: distorted (normalized) point coordinates in the image plane (2xN matrix) +% k: Distortion coefficients (radial and tangential) (4x1 vector) +% +%OUTPUT: x: undistorted (normalized) point coordinates in the image plane (2xN matrix) +% +%Method: Iterative method for compensation. +% +%NOTE: This compensation has to be done after the subtraction +% of the principal point, and division by the focal length. + + +if length(k) < 4, + + [x] = comp_distortion(xd,k); + +else + + + k1 = k(1); + k2 = k(2); + p1 = k(3); + p2 = k(4); + + x = xd; % initial guess + + for kk=1:5; + + r_2 = sum(x.^2); + k_radial = 1 + k1 * r_2 + k2 * r_2.^2; + delta_x = [2*p1*x(1,:).*x(2,:) + p2*(r_2 + 2*x(1,:).^2) ; + p1 * (r_2 + 2*x(2,:).^2)+2*p2*x(1,:).*x(2,:)]; + x = (xd - delta_x)./(ones(2,1)*k_radial); + + end; + +end; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_error_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_error_calib.m new file mode 100755 index 0000000..c7bf662 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/comp_error_calib.m @@ -0,0 +1,46 @@ +%%%%%%%%%%%%%%%%%%%% RECOMPUTES THE REPROJECTION ERROR %%%%%%%%%%%%%%%%%%%%%%%% + +check_active_images; + +% Reproject the patterns on the images, and compute the pixel errors: + +ex = []; % Global error vector +x = []; % Detected corners on the image plane +y = []; % Reprojected points + +if ~exist('alpha_c'), + alpha_c = 0; +end; + +for kk = 1:n_ima, + + eval(['omckk = omc_' num2str(kk) ';']); + eval(['Tckk = Tc_' num2str(kk) ';']); + + if active_images(kk) & (~isnan(omckk(1,1))), + + %Rkk = rodrigues(omckk); + + eval(['y_' num2str(kk) ' = project_points2(X_' num2str(kk) ',omckk,Tckk,fc,cc,kc,alpha_c);']); + + eval(['ex_' num2str(kk) ' = x_' num2str(kk) ' -y_' num2str(kk) ';']); + + eval(['x_kk = x_' num2str(kk) ';']); + + eval(['ex = [ex ex_' num2str(kk) '];']); + eval(['x = [x x_' num2str(kk) '];']); + eval(['y = [y y_' num2str(kk) '];']); + + else + + % eval(['y_' num2str(kk) ' = NaN*ones(2,1);']); + + + % If inactivated image, the error does not make sense: + eval(['ex_' num2str(kk) ' = NaN*ones(2,1);']); + + end; + +end; + +err_std = std(ex')'; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_collineation.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_collineation.m new file mode 100755 index 0000000..809c309 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_collineation.m @@ -0,0 +1,66 @@ +function [H,Hnorm,inv_Hnorm] = compute_collineation (a00, a10, a11, a01); + +% new formalism using homographies + +a00 = a00 / a00(3); +a10 = a10 / a10(3); +a11 = a11 / a11(3); +a01 = a01 / a01(3); + + +% Prenormalization of point coordinates (very important): +% (Affine normalization) + +ax = [a00(1);a10(1);a11(1);a01(1)]; +ay = [a00(2);a10(2);a11(2);a01(2)]; + +mxx = mean(ax); +myy = mean(ay); +ax = ax - mxx; +ay = ay - myy; + +scxx = mean(abs(ax)); +scyy = mean(abs(ay)); + + +Hnorm = [1/scxx 0 -mxx/scxx;0 1/scyy -myy/scyy;0 0 1]; +inv_Hnorm = [scxx 0 mxx ; 0 scyy myy; 0 0 1]; + + +a00n = Hnorm*a00; +a10n = Hnorm*a10; +a11n = Hnorm*a11; +a01n = Hnorm*a01; + + +% Computation of the vanishing points: + +V1n = cross(cross(a00n,a10n),cross(a01n,a11n)); +V2n = cross(cross(a00n,a01n),cross(a10n,a11n)); + +V1 = inv_Hnorm*V1n; +V2 = inv_Hnorm*V2n; + + +% Normalizaion of the vanishing points: + +V1n = V1n/norm(V1n); +V2n = V2n/norm(V2n); + + +% Closed-form solution of the coefficients: + +alpha_x = (a10n(2)*a00n(1) - a10n(1)*a00n(2))/(V1n(2)*a10n(1)-V1n(1)*a10n(2)); + +alpha_y = (a01n(2)*a00n(1) - a01n(1)*a00n(2))/(V2n(2)*a01n(1)-V2n(1)*a01n(2)); + + +% Remaining Homography + +Hrem = [alpha_x*V1n alpha_y*V2n a00n]; + + +% Final homography: + +H = inv_Hnorm*Hrem; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic.m new file mode 100755 index 0000000..5217351 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic.m @@ -0,0 +1,123 @@ +function [omckk,Tckk,Rckk,H,x,ex,JJ] = compute_extrinsic(x_kk,X_kk,fc,cc,kc,alpha_c,MaxIter,thresh_cond), + +%compute_extrinsic +% +%[omckk,Tckk,Rckk,H,x,ex] = compute_extrinsic(x_kk,X_kk,fc,cc,kc,alpha_c) +% +%Computes the extrinsic parameters attached to a 3D structure X_kk given its projection +%on the image plane x_kk and the intrinsic camera parameters fc, cc and kc. +%Works with planar and non-planar structures. +% +%INPUT: x_kk: Feature locations on the images +% X_kk: Corresponding grid coordinates +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% alpha_c: Skew coefficient +% +%OUTPUT: omckk: 3D rotation vector attached to the grid positions in space +% Tckk: 3D translation vector attached to the grid positions in space +% Rckk: 3D rotation matrices corresponding to the omc vectors +% H: Homography between points on the grid and points on the image plane (in pixel) +% This makes sense only if the planar that is used in planar. +% x: Reprojections of the points on the image plane +% ex: Reprojection error: ex = x_kk - x; +% +%Method: Computes the normalized point coordinates, then computes the 3D pose +% +%Important functions called within that program: +% +%normalize: Computes the normalize image point coordinates. +% +%pose3D: Computes the 3D pose of the structure given the normalized image projection. +% +%project_points.m: Computes the 2D image projections of a set of 3D points + + + +if nargin < 8, + thresh_cond = inf; +end; + + +if nargin < 7, + MaxIter = 20; +end; + + +if nargin < 6, + alpha_c = 0; + if nargin < 5, + kc = zeros(4,1); + if nargin < 4, + cc = zeros(2,1); + if nargin < 3, + fc = ones(2,1); + if nargin < 2, + error('Need 2D projections and 3D points (in compute_extrinsic.m)'); + return; + end; + end; + end; + end; +end; + +% Initialization: + +[omckk,Tckk,Rckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc,alpha_c); + +% Refinement: + +[omckk,Tckk,Rckk,JJ] = compute_extrinsic_refine(omckk,Tckk,x_kk,X_kk,fc,cc,kc,alpha_c,MaxIter,thresh_cond); + + +% computation of the homography (not useful in the end) + +H = [Rckk(:,1:2) Tckk]; + +% Computes the reprojection error in pixels: + +x = project_points2(X_kk,omckk,Tckk,fc,cc,kc,alpha_c); + +ex = x_kk - x; + + +% Converts the homography in pixel units: + +KK = [fc(1) alpha_c*fc(1) cc(1);0 fc(2) cc(2); 0 0 1]; + +H = KK*H; + + + + +return; + + +% Test of compte extrinsic: + +Np = 4; +sx = 10; +sy = 10; +sz = 5; + +om = randn(3,1); +T = [0;0;100]; + +noise = 2/1000; + +XX = [sx*randn(1,Np);sy*randn(1,Np);sz*randn(1,Np)]; +xx = project_points(XX,om,T); + +xxn = xx + noise * randn(2,Np); + +[omckk,Tckk] = compute_extrinsic(xxn,XX); + +[om omckk om-omckk] +[T Tckk T-Tckk] + +figure(3); +plot(xx(1,:),xx(2,:),'r+'); +hold on; +plot(xxn(1,:),xxn(2,:),'g+'); +hold off; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_init.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_init.m new file mode 100755 index 0000000..2e6d821 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_init.m @@ -0,0 +1,151 @@ +function [omckk,Tckk,Rckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc,alpha_c), + +%compute_extrinsic +% +%[omckk,Tckk,Rckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc,alpha_c) +% +%Computes the extrinsic parameters attached to a 3D structure X_kk given its projection +%on the image plane x_kk and the intrinsic camera parameters fc, cc and kc. +%Works with planar and non-planar structures. +% +%INPUT: x_kk: Feature locations on the images +% X_kk: Corresponding grid coordinates +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% alpha_c: Skew coefficient +% +%OUTPUT: omckk: 3D rotation vector attached to the grid positions in space +% Tckk: 3D translation vector attached to the grid positions in space +% Rckk: 3D rotation matrices corresponding to the omc vectors +% +%Method: Computes the normalized point coordinates, then computes the 3D pose +% +%Important functions called within that program: +% +%normalize: Computes the normalize image point coordinates. +% +%pose3D: Computes the 3D pose of the structure given the normalized image projection. +% +%project_points.m: Computes the 2D image projections of a set of 3D points + + + +if nargin < 6, + alpha_c = 0; + if nargin < 5, + kc = zeros(4,1); + if nargin < 4, + cc = zeros(2,1); + if nargin < 3, + fc = ones(2,1); + if nargin < 2, + error('Need 2D projections and 3D points (in compute_extrinsic.m)'); + return; + end; + end; + end; + end; +end; + + +% Compute the normalized coordinates: + +xn = normalize(x_kk,fc,cc,kc,alpha_c); + + + +Np = size(xn,2); + +%% Check for planarity of the structure: + +X_mean = mean(X_kk')'; + +Y = X_kk - (X_mean*ones(1,Np)); + +YY = Y*Y'; + +[U,S,V] = svd(YY); + +r = S(3,3)/S(2,2); + +if (r < 1e-3)|(Np < 6), %1e-3, %1e-4, %norm(X_kk(3,:)) < eps, % Test of planarity + + %fprintf(1,'Planar structure detected: r=%f\n',r); + + % Transform the plane to bring it in the Z=0 plane: + + R_transform = V'; + + if det(R_transform) < 0, R_transform = -R_transform; end; + + T_transform = -(R_transform)*X_mean; + + X_new = R_transform*X_kk + T_transform*ones(1,Np); + + + % Compute the planar homography: + + H = compute_homography (xn,X_new(1:2,:)); + + % De-embed the motion parameters from the homography: + + sc = mean([norm(H(:,1));norm(H(:,2))]); + + H = H/sc; + + omckk = rodrigues([H(:,1:2) cross(H(:,1),H(:,2))]); + Rckk = rodrigues(omckk); + Tckk = H(:,3); + + %If Xc = Rckk * X_new + Tckk, then Xc = Rckk * R_transform * X_kk + Tckk + T_transform + + Tckk = Tckk + Rckk* T_transform; + Rckk = Rckk * R_transform; + + omckk = rodrigues(Rckk); + Rckk = rodrigues(omckk); + + +else + + %fprintf(1,'Non planar structure detected: r=%f\n',r); + + % Computes an initial guess for extrinsic parameters (works for general 3d structure, not planar!!!): + % The DLT method is applied here!! + + J = zeros(2*Np,12); + + xX = (ones(3,1)*xn(1,:)).*X_kk; + yX = (ones(3,1)*xn(2,:)).*X_kk; + + J(1:2:end,[1 4 7]) = -X_kk'; + J(2:2:end,[2 5 8]) = X_kk'; + J(1:2:end,[3 6 9]) = xX'; + J(2:2:end,[3 6 9]) = -yX'; + J(1:2:end,12) = xn(1,:)'; + J(2:2:end,12) = -xn(2,:)'; + J(1:2:end,10) = -ones(Np,1); + J(2:2:end,11) = ones(Np,1); + + JJ = J'*J; + [U,S,V] = svd(JJ); + + RR = reshape(V(1:9,12),3,3); + + if det(RR) < 0, + V(:,12) = -V(:,12); + RR = -RR; + end; + + [Ur,Sr,Vr] = svd(RR); + + Rckk = Ur*Vr'; + + sc = norm(V(1:9,12)) / norm(Rckk(:)); + Tckk = V(10:12,12)/sc; + + omckk = rodrigues(Rckk); + Rckk = rodrigues(omckk); + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_refine.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_refine.m new file mode 100755 index 0000000..a4d066c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_extrinsic_refine.m @@ -0,0 +1,113 @@ +function [omckk,Tckk,Rckk,JJ] = compute_extrinsic_refine(omc_init,Tc_init,x_kk,X_kk,fc,cc,kc,alpha_c,MaxIter,thresh_cond), + +%compute_extrinsic +% +%[omckk,Tckk,Rckk] = compute_extrinsic_refine(x_kk,X_kk,fc,cc,kc,alpha_c,MaxIter) +% +%Computes the extrinsic parameters attached to a 3D structure X_kk given its projection +%on the image plane x_kk and the intrinsic camera parameters fc, cc and kc. +%Works with planar and non-planar structures. +% +%INPUT: x_kk: Feature locations on the images +% X_kk: Corresponding grid coordinates +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% alpha_c: Skew coefficient +% MaxIter: Maximum number of iterations +% +%OUTPUT: omckk: 3D rotation vector attached to the grid positions in space +% Tckk: 3D translation vector attached to the grid positions in space +% Rckk: 3D rotation matrices corresponding to the omc vectors + +% +%Method: Computes the normalized point coordinates, then computes the 3D pose +% +%Important functions called within that program: +% +%normalize: Computes the normalize image point coordinates. +% +%pose3D: Computes the 3D pose of the structure given the normalized image projection. +% +%project_points.m: Computes the 2D image projections of a set of 3D points + + +if nargin < 10, + thresh_cond = inf; +end; + + +if nargin < 9, + MaxIter = 20; +end; + +if nargin < 8, + alpha_c = 0; + if nargin < 7, + kc = zeros(4,1); + if nargin < 6, + cc = zeros(2,1); + if nargin < 5, + fc = ones(2,1); + if nargin < 4, + error('Need 2D projections and 3D points (in compute_extrinsic_refine.m)'); + return; + end; + end; + end; + end; +end; + + +% Initialization: + +omckk = omc_init; +Tckk = Tc_init; + + +% Final optimization (minimize the reprojection error in pixel): +% through Gradient Descent: + +param = [omckk;Tckk]; + +change = 1; + +iter = 0; + +%keyboard; + +%fprintf(1,'Gradient descent iterations: '); + +while (change > 1e-10)&(iter < MaxIter), + + %fprintf(1,'%d...',iter+1); + + [x,dxdom,dxdT] = project_points2(X_kk,omckk,Tckk,fc,cc,kc,alpha_c); + + ex = x_kk - x; + + %keyboard; + + JJ = [dxdom dxdT]; + + if cond(JJ) > thresh_cond, + change = 0; + else + + JJ2 = JJ'*JJ; + + param_innov = inv(JJ2)*(JJ')*ex(:); + param_up = param + param_innov; + change = norm(param_innov)/norm(param_up); + param = param_up; + iter = iter + 1; + + omckk = param(1:3); + Tckk = param(4:6); + end; + +end; + +%fprintf(1,'\n'); + +Rckk = rodrigues(omckk); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_homography.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_homography.m new file mode 100755 index 0000000..fcc9003 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/compute_homography.m @@ -0,0 +1,163 @@ +function [H,Hnorm,inv_Hnorm] = compute_homography (m,M); + +%compute_homography +% +%[H,Hnorm,inv_Hnorm] = compute_homography (m,M) +% +%Computes the planar homography between the point coordinates on the plane (M) and the image +%point coordinates (m). +% +%INPUT: m: homogeneous coordinates in the image plane (3xN matrix) +% M: homogeneous coordinates in the plane in 3D (3xN matrix) +% +%OUTPUT: H: Homography matrix (3x3 homogeneous matrix) +% Hnorm: Normlization matrix used on the points before homography computation +% (useful for numerical stability is points in pixel coordinates) +% inv_Hnorm: The inverse of Hnorm +% +%Definition: m ~ H*M where "~" means equal up to a non zero scalar factor. +% +%Method: First computes an initial guess for the homography through quasi-linear method. +% Then, if the total number of points is larger than 4, optimize the solution by minimizing +% the reprojection error (in the least squares sense). +% +% +%Important functions called within that program: +% +%comp_distortion_oulu: Undistorts pixel coordinates. +% +%compute_homography.m: Computes the planar homography between points on the grid in 3D, and the image plane. +% +%project_points.m: Computes the 2D image projections of a set of 3D points, and also returns te Jacobian +% matrix (derivative with respect to the intrinsic and extrinsic parameters). +% This function is called within the minimization loop. + + + + +Np = size(m,2); + +if size(m,1)<3, + m = [m;ones(1,Np)]; +end; + +if size(M,1)<3, + M = [M;ones(1,Np)]; +end; + + +m = m ./ (ones(3,1)*m(3,:)); +M = M ./ (ones(3,1)*M(3,:)); + +% Prenormalization of point coordinates (very important): +% (Affine normalization) + +ax = m(1,:); +ay = m(2,:); + +mxx = mean(ax); +myy = mean(ay); +ax = ax - mxx; +ay = ay - myy; + +scxx = mean(abs(ax)); +scyy = mean(abs(ay)); + + +Hnorm = [1/scxx 0 -mxx/scxx;0 1/scyy -myy/scyy;0 0 1]; +inv_Hnorm = [scxx 0 mxx ; 0 scyy myy; 0 0 1]; + +mn = Hnorm*m; + +% Compute the homography between m and mn: + +% Build the matrix: + +L = zeros(2*Np,9); + +L(1:2:2*Np,1:3) = M'; +L(2:2:2*Np,4:6) = M'; +L(1:2:2*Np,7:9) = -((ones(3,1)*mn(1,:)).* M)'; +L(2:2:2*Np,7:9) = -((ones(3,1)*mn(2,:)).* M)'; + +if Np > 4, + L = L'*L; +end; + +[U,S,V] = svd(L); + +hh = V(:,9); +hh = hh/hh(9); + +Hrem = reshape(hh,3,3)'; +%Hrem = Hrem / Hrem(3,3); + +% Final homography: + +H = inv_Hnorm*Hrem; + + +%%% Homography refinement if there are more than 4 points: + +if Np > 4, + + % Final refinement: + + hhv = reshape(H',9,1); + hhv = hhv(1:8); + + for iter=1:10, + + mrep = H * M; + + J = zeros(2*Np,8); + + MMM = (M ./ (ones(3,1)*mrep(3,:))); + + J(1:2:2*Np,1:3) = -MMM'; + J(2:2:2*Np,4:6) = -MMM'; + + mrep = mrep ./ (ones(3,1)*mrep(3,:)); + + m_err = m(1:2,:) - mrep(1:2,:); + m_err = m_err(:); + + MMM2 = (ones(3,1)*mrep(1,:)) .* MMM; + MMM3 = (ones(3,1)*mrep(2,:)) .* MMM; + + J(1:2:2*Np,7:8) = MMM2(1:2,:)'; + J(2:2:2*Np,7:8) = MMM3(1:2,:)'; + + MMM = (M ./ (ones(3,1)*mrep(3,:)))'; + + hh_innov = inv(J'*J)*J'*m_err; + + hhv_up = hhv - hh_innov; + + H_up = reshape([hhv_up;1],3,3)'; + + %norm(m_err) + %norm(hh_innov) + + hhv = hhv_up; + H = H_up; + + end; + +end; + + + + + +return; + +%test of Jacobian + +mrep = H*M; +mrep = mrep ./ (ones(3,1)*mrep(3,:)); + +m_err = mrep(1:2,:) - m(1:2,:); +figure(8); +plot(m_err(1,:),m_err(2,:),'r+'); +std(m_err') diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/cornerfinder.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/cornerfinder.m new file mode 100755 index 0000000..9bfa51f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/cornerfinder.m @@ -0,0 +1,215 @@ +function [xc,good,bad,type] = cornerfinder(xt,I,wintx,winty,wx2,wy2); + +%[xc] = cornerfinder(xt,I); +% +%Finds the sub-pixel corners on the image I with initial guess xt +%xt and xc are 2xN matrices. The first component is the x coordinate +%(horizontal) and the second component is the y coordinate (vertical) +% +%Based on Harris corner finder method +% +%Finds corners to a precision below .1 pixel! +%Oct. 14th, 1997 - UPDATED to work with vertical and horizontal edges as well!!! +%Sept 1998 - UPDATED to handle diverged points: we keep the original points +%good is a binary vector indicating wether a feature point has been properly +%found. +% +%Add a zero zone of size wx2,wy2 +%July 15th, 1999 - Bug on the mask building... fixed + change to Gaussian mask with higher +%resolution and larger number of iterations. + + +% California Institute of Technology +% (c) Jean-Yves Bouguet -- Oct. 14th, 1997 + + + +line_feat = 1; % set to 1 to allow for extraction of line features. + +xt = xt'; +xt = fliplr(xt); + + +if nargin < 4, + winty = 5; + if nargin < 3, + wintx = 5; + end; +end; + + +if nargin < 6, + wx2 = -1; + wy2 = -1; +end; + + +%mask = ones(2*wintx+1,2*winty+1); +mask = exp(-((-wintx:wintx)'/(wintx)).^2) * exp(-((-winty:winty)/(winty)).^2); + + +if (wx2>0) & (wy2>0), + if ((wintx - wx2)>=2)&((winty - wy2)>=2), + mask(wintx+1-wx2:wintx+1+wx2,winty+1-wy2:winty+1+wy2)= zeros(2*wx2+1,2*wy2+1); + end; +end; + +offx = [-wintx:wintx]'*ones(1,2*winty+1); +offy = ones(2*wintx+1,1)*[-winty:winty]; + +resolution = 0.005; + +MaxIter = 10; + +[nx,ny] = size(I); +N = size(xt,1); + +xc = xt; % first guess... they don't move !!! + +type = zeros(1,N); + + +for i=1:N, + + v_extra = resolution + 1; % just larger than resolution + + compt = 0; % no iteration yet + + while (norm(v_extra) > resolution) & (compt 0, % the sub pixel + vIx = [itIx 1-itIx 0]'; % accuracy. + else + vIx = [0 1+itIx -itIx]'; + end; + if itIy > 0, + vIy = [itIy 1-itIy 0]; + else + vIy = [0 1+itIy -itIy]; + end; + + + % What if the sub image is not in? + + if (crIx-wintx-2 < 1), xmin=1; xmax = 2*wintx+5; + elseif (crIx+wintx+2 > nx), xmax = nx; xmin = nx-2*wintx-4; + else + xmin = crIx-wintx-2; xmax = crIx+wintx+2; + end; + + if (crIy-winty-2 < 1), ymin=1; ymax = 2*winty+5; + elseif (crIy+winty+2 > ny), ymax = ny; ymin = ny-2*winty-4; + else + ymin = crIy-winty-2; ymax = crIy+winty+2; + end; + + + SI = I(xmin:xmax,ymin:ymax); % The necessary neighborhood + SI = conv2(conv2(SI,vIx,'same'),vIy,'same'); + SI = SI(2:2*wintx+4,2:2*winty+4); % The subpixel interpolated neighborhood + [gy,gx] = gradient(SI); % The gradient image + gx = gx(2:2*wintx+2,2:2*winty+2); % extraction of the useful parts only + gy = gy(2:2*wintx+2,2:2*winty+2); % of the gradients + + px = cIx + offx; + py = cIy + offy; + + gxx = gx .* gx .* mask; + gyy = gy .* gy .* mask; + gxy = gx .* gy .* mask; + + + bb = [sum(sum(gxx .* px + gxy .* py)); sum(sum(gxy .* px + gyy .* py))]; + + a = sum(sum(gxx)); + b = sum(sum(gxy)); + c = sum(sum(gyy)); + + dt = a*c - b^2; + + xc2 = [c*bb(1)-b*bb(2) a*bb(2)-b*bb(1)]/dt; + + + %keyboard; + + if line_feat, + + G = [a b;b c]; + [U,S,V] = svd(G); + + %keyboard; + + % If non-invertible, then project the point onto the edge orthogonal: + + if (S(1,1)/S(2,2) > 50), + % projection operation: + xc2 = xc2 + sum((xc(i,:)-xc2).*(V(:,2)'))*V(:,2)'; + type(i) = 1; + end; + + end; + + + %keyboard; + +% G = [a b;b c]; +% [U,S,V] = svd(G); + + +% if S(1,1)/S(2,2) > 150, +% bb2 = U'*bb; +% xc2 = (V*[bb2(1)/S(1,1) ;0])'; +% else +% xc2 = [c*bb(1)-b*bb(2) a*bb(2)-b*bb(1)]/dt; +% end; + + + %if (abs(a)> 50*abs(c)), +% xc2 = [(c*bb(1)-b*bb(2))/dt xc(i,2)]; +% elseif (abs(c)> 50*abs(a)) +% xc2 = [xc(i,1) (a*bb(2)-b*bb(1))/dt]; +% else +% xc2 = [c*bb(1)-b*bb(2) a*bb(2)-b*bb(1)]/dt; +% end; + + %keyboard; + + v_extra = xc(i,:) - xc2; + + xc(i,:) = xc2; + +% keyboard; + + compt = compt + 1; + + end +end; + + +% check for points that diverge: + +delta_x = xc(:,1) - xt(:,1); +delta_y = xc(:,2) - xt(:,2); + +%keyboard; + + +bad = (abs(delta_x) > wintx) | (abs(delta_y) > winty); +good = ~bad; +in_bad = find(bad); + +% For the diverged points, keep the original guesses: + +xc(in_bad,:) = xt(in_bad,:); + +xc = fliplr(xc); +xc = xc'; + +bad = bad'; +good = good'; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/count_squares.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/count_squares.m new file mode 100755 index 0000000..0e226c0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/count_squares.m @@ -0,0 +1,74 @@ +function ns = count_squares(I,x1,y1,x2,y2,win); + +%keyboard; + +[ny,nx] = size(I); + +lambda = [y1 - y2;x2 - x1;x1*y2 - x2*y1]; + +lambda = 1/sqrt(lambda(1)^2 + lambda(2)^2) * lambda; + +l1 = lambda + [0;0;win]; +l2 = lambda - [0;0;win]; + + +dx = x2-x1; +dy = y2 - y1; + + +if abs(dx) > abs(dy), + + if x2 > x1, + xs = x1:x2; + else + xs = x1:-1:x2; + end; + + ys = -(lambda(3) + lambda(1)*xs)/lambda(2); + +else + + if y2 > y1, + ys = y1:y2; + else + ys = y1:-1:y2; + end; + xs = -(lambda(3) + lambda(2)*ys)/lambda(1); + +end; + + + + Np = length(xs); + + xs_mat = ones(2*win + 1,1)*xs; + ys_mat = ones(2*win + 1,1)*ys; + + win_mat = (-win:win)'*ones(1,Np); + + + xs_mat2 = round(xs_mat - win_mat * lambda(1)); + ys_mat2 = round(ys_mat - win_mat * lambda(2)); + + ind_mat = (xs_mat2 - 1) * ny + ys_mat2; + + ima_patch = zeros(2*win + 1,Np); + + ima_patch(:) = I(ind_mat(:)); + + %ima2 = ima_patch(:,win+1:end-win); + + filtk = [ones(win,Np);zeros(1,Np);-ones(win,Np)]; + + out_f = sum(filtk.*ima_patch); + + out_f_f = conv2(out_f,[1/4 1/2 1/4],'same'); + + out_f_f = out_f_f(win+1:end-win); + + ns = length(find(((out_f_f(2:end)>=0)&(out_f_f(1:end-1)<0)) | ((out_f_f(2:end)<=0)&(out_f_f(1:end-1)>0))))+1; + + + + +return; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/data_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/data_calib.m new file mode 100755 index 0000000..422769b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/data_calib.m @@ -0,0 +1,92 @@ +%%% This script alets the user enter the name of the images (base name, numbering scheme,... + + +% Checks that there are some images in the directory: + +l_ras = dir('*ras'); +s_ras = size(l_ras,1); +l_bmp = dir('*bmp'); +s_bmp = size(l_bmp,1); +l_tif = dir('*tif'); +s_tif = size(l_tif,1); +l_pgm = dir('*pgm'); +s_pgm = size(l_pgm,1); +l_jpg = dir('*jpg'); +s_jpg = size(l_jpg,1); + +s_tot = s_ras + s_bmp + s_tif + s_pgm + s_jpg; + +if s_tot < 1, + fprintf(1,'No image in this directory in either ras, bmp, tif, pgm or jpg format. Change directory and try again.\n'); + break; +end; + + +% IF yes, display the directory content: + +dir; + +Nima_valid = 0; + +while (Nima_valid==0), + + fprintf(1,'\n'); + calib_name = input('Basename camera calibration images (without number nor suffix): ','s'); + + format_image = '0'; + + while format_image == '0', + + format_image = input('Image format: ([]=''r''=''ras'', ''b''=''bmp'', ''t''=''tif'', ''p''=''pgm'', ''j''=''jpg'', ''m''=''ppm'') ','s'); + + if isempty(format_image), + format_image = 'ras'; + end; + + if lower(format_image(1)) == 'm', + format_image = 'ppm'; + else + if lower(format_image(1)) == 'b', + format_image = 'bmp'; + else + if lower(format_image(1)) == 't', + format_image = 'tif'; + else + if lower(format_image(1)) == 'p', + format_image = 'pgm'; + else + if lower(format_image(1)) == 'j', + format_image = 'jpg'; + else + if lower(format_image(1)) == 'r', + format_image = 'ras'; + else + disp('Invalid image format'); + format_image = '0'; % Ask for format once again + end; + end; + end; + end; + end; + end; + end; + + + check_directory; + +end; + + + +%string_save = 'save calib_data n_ima type_numbering N_slots image_numbers format_image calib_name first_num'; + +%eval(string_save); + + + +if (Nima_valid~=0), + % Reading images: + + ima_read_calib; % may be launched from the toolbox itself +end; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/error_analysis.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/error_analysis.m new file mode 100755 index 0000000..85feac5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/error_analysis.m @@ -0,0 +1,182 @@ +%%% ERROR_ANALYSIS +%%% This simulation helps coputing the acturacies of calibration +%%% Run it after the main calibration + + + +N_runs = 200; + +%N_ima_active = 4; + +saving = 1; + +if 1, %~exist('fc_list'), % initialization + + % Initialization: + + load Calib_Results; + check_active_images; + + fc_list = []; + cc_list = []; + kc_list = []; + active_images_list = []; + + + for kk=1:n_ima, + + eval(['omc_list_' num2str(kk) ' = [];']); + eval(['Tc_list_' num2str(kk) ' = [];']); + + end; + + %sx = median(abs(ex(1,:)))*1.4836; + %sy = median(abs(ex(2,:)))*1.4836; + + sx = std(ex(1,:)); + sy = std(ex(2,:)); + + % Saving the feature locations: + + for kk = 1:n_ima, + + eval(['x_save_' num2str(kk) ' = x_' num2str(kk) ';']); + eval(['y_save_' num2str(kk) ' = y_' num2str(kk) ';']); + + end; + + active_images_save = active_images; + ind_active_save = ind_active; + + fc_save = fc; + cc_save = cc; + kc_save = kc; + KK_save = KK; + + +end; + + + + +%%% The main loop: + + +for ntrial = 1:N_runs, + + fprintf(1,'\nRun number: %d\n',ntrial); + fprintf(1, '----------\n'); + + for kk = 1:n_ima, + + eval(['y_kk = y_save_' num2str(kk) ';']) + + if active_images(kk) & ~isnan(y_kk(1,1)), + + Nkk = size(y_kk,2); + + x_kk_new = y_kk + [sx * randn(1,Nkk);sy*randn(1,Nkk)]; + + eval(['x_' num2str(kk) ' = x_kk_new;']); + + end; + + end; + + N_active = length(ind_active_save); + junk = randn(1,N_active); + [junk,junk2] = sort(junk); + + active_images = zeros(1,n_ima); + active_images(ind_active_save(junk2(1:N_ima_active))) = ones(1,N_ima_active); + + fc = fc_save; + cc = cc_save; + kc = kc_save; + KK = KK_save; + + go_calib_optim; + + fc_list = [fc_list fc]; + cc_list = [cc_list cc]; + kc_list = [kc_list kc]; + active_images_list = [active_images_list active_images']; + + for kk=1:n_ima, + + eval(['omc_list_' num2str(kk) ' = [ omc_list_' num2str(kk) ' omc_' num2str(kk) ' ];']); + eval(['Tc_list_' num2str(kk) ' = [ Tc_list_' num2str(kk) ' Tc_' num2str(kk) ' ];']); + + end; + +end; + + + + +if 0, + +% Restoring the feature locations: + +for kk = 1:n_ima, + + eval(['x_' num2str(kk) ' = x_save_' num2str(kk) ';']); + +end; + +fprintf(1,'\nFinal run (with the real data)\n'); +fprintf(1, '------------------------------\n'); + +active_images = active_images_save; +ind_active = ind_active_save; + +go_calib_optim; + +fc_list = [fc_list fc]; +cc_list = [cc_list cc]; +kc_list = [kc_list kc]; +active_images_list = [active_images_list active_images']; + +for kk=1:n_ima, + + eval(['omc_list_' num2str(kk) ' = [ omc_list_' num2str(kk) ' omc_' num2str(kk) ' ];']); + eval(['Tc_list_' num2str(kk) ' = [ Tc_list_' num2str(kk) ' Tc_' num2str(kk) ' ];']); + +end; + +end; + + + + + +if saving, + +disp(['Save Calibration accuracy results under Calib_Accuracies_' num2str(N_ima_active) '.mat']); + +string_save = ['save Calib_Accuracies_' num2str(N_ima_active) ' active_images n_ima N_ima_active N_runs active_images_list fc cc kc fc_list cc_list kc_list']; + +for kk = 1:n_ima, + string_save = [string_save ' Tc_list_' num2str(kk) ' omc_list_' num2str(kk) ' Tc_' num2str(kk) ' omc_' num2str(kk) ]; +end; + +eval(string_save); + +end; + + +return; + +std(fc_list') + +std(cc_list') + +std(kc_list') + +for kk = 1:n_ima, + + eval(['std(Tc_list_' num2str(kk) ''')']) + +end; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/export_calib_data.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/export_calib_data.m new file mode 100755 index 0000000..39506a8 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/export_calib_data.m @@ -0,0 +1,99 @@ +%% Export calibration data (corners + 3D coordinates) to +%% text files (in Willson-Heikkila's format or Zhang's format) + +%% Thanks to Michael Goesele (from the Max-Planck-Institut) for the original suggestion +%% of adding thsi export function to the toolbox. + + +if ~exist('n_ima'), + fprintf(1,'ERROR: No calibration data to export\n'); + +else + + check_active_images; + + check_extracted_images; + + check_active_images; + + fprintf(1,'Tool that exports calibration data to Willson-Heikkila or Zhang formats\n'); + + qformat = -1; + + while (qformat ~=0)&(qformat ~=1), + + fprintf(1,'Two possible formats of export: 0=Willson and Heikkila, 1=Zhang\n') + qformat = input('Format of export (enter 0 or 1): '); + + if isempty(qformat) + qformat = -1; + end; + + if (qformat ~=0)&(qformat ~=1), + + fprintf(1,'Invalid entry. Try again.\n') + + end; + + end; + + if qformat == 0, + + fprintf(1,'\nExport of calibration data to text files (Willson and Heikkila''s format)\n'); + outputfile = input('File basename: ','s'); + + for kk = ind_active, + + eval(['X_kk = X_' num2str(kk) ';']); + eval(['x_kk = x_' num2str(kk) ';']); + + Xx = [X_kk ; x_kk]'; + + file_name = [outputfile num2str(kk)]; + + disp(['Exporting calibration data (3D world + 2D image coordinates) of image ' num2str(kk) ' to file ' file_name '...']); + + eval(['save ' file_name ' Xx -ASCII']); + + end; + + else + + fprintf(1,'\nExport of calibration data to text files (Zhang''s format)\n'); + modelfile = input('File basename for the 3D world coordinates: ','s'); + datafile = input('File basename for the 2D image coordinates: ','s'); + + for kk = ind_active, + + eval(['X_kk = X_' num2str(kk) ';']); + eval(['x_kk = x_' num2str(kk) ';']); + + if ~exist(['n_sq_x_' num2str(kk)]), + n_sq_x = 1; + n_sq_y = size(X_kk,2); + else + eval(['n_sq_x = n_sq_x_' num2str(kk) ';']); + eval(['n_sq_y = n_sq_y_' num2str(kk) ';']); + end; + + X = reshape(X_kk(1,:)',n_sq_x+1,n_sq_y+1)'; + Y = reshape(X_kk(2,:)',n_sq_x+1,n_sq_y+1)'; + XY = reshape([X;Y],n_sq_y+1,2*(n_sq_x+1)); + + x = reshape(x_kk(1,:)',n_sq_x+1,n_sq_y+1)'; + y = reshape(x_kk(2,:)',n_sq_x+1,n_sq_y+1)'; + xy = reshape([x;y],n_sq_y+1,2*(n_sq_x+1)); + + disp(['Exporting calibration data of image ' num2str(kk) ' to files ' modelfile num2str(kk) '.txt and ' datafile num2str(kk) '.txt...']); + + eval(['save ' modelfile num2str(kk) '.txt XY -ASCII']); + eval(['save ' datafile num2str(kk) '.txt xy -ASCII']); + + end; + + +end; + +fprintf(1,'done\n'); + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ext_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ext_calib.m new file mode 100755 index 0000000..04d6319 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ext_calib.m @@ -0,0 +1,152 @@ + +%%%%%%%%%%%%%%%%%%%% SHOW EXTRINSIC RESULTS %%%%%%%%%%%%%%%%%%%%%%%% + +if ~exist('n_ima')|~exist('fc'), + fprintf(1,'No calibration data available.\n'); + return; +end; + +check_active_images; + +if ~exist(['omc_' num2str(ind_active(1))]), + fprintf(1,'No calibration data available.\n'); + return; +end; + +%if ~exist('no_grid'), + no_grid = 0; +%end; + +if ~exist(['n_sq_x_' num2str(ind_active(1))]), + no_grid = 1; +end; + + +if 0, + +err_std = std(ex'); + +fprintf(1,'\n\nCalibration results without principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); + +end; + + +% Color code for each image: + +colors = 'brgkcm'; + + +%%% Show the extrinsic parameters + +if ~exist('dX'), + eval(['dX = norm(Tc_' num2str(ind_active(1)) ')/10;']); + dY = dX; +end; + +IP = 5*dX*([0 nx-1 nx-1 0 0 ; 0 0 ny-1 ny-1 0;1 1 1 1 1] - [cc;0]*ones(1,5)) ./ ([fc;1]*ones(1,5)); +BASE = 5*dX*([0 1 0 0 0 0;0 0 0 1 0 0;0 0 0 0 0 1]); +IP = reshape([IP;BASE(:,1)*ones(1,5);IP],3,15); + +figure(4); +[a,b] = view; + +figure(4); +plot3(BASE(1,:),BASE(3,:),-BASE(2,:),'b-','linewidth',2'); +hold on; +plot3(IP(1,:),IP(3,:),-IP(2,:),'r-','linewidth',2); +text(6*dX,0,0,'X_c'); +text(-dX,5*dX,0,'Z_c'); +text(0,0,-6*dX,'Y_c'); +text(-dX,-dX,dX,'O_c'); + + +for kk = 1:n_ima, + + if active_images(kk); + + if exist(['X_' num2str(kk)]) & exist(['omc_' num2str(kk)]), + + eval(['XX_kk = X_' num2str(kk) ';']); + + if ~isnan(XX_kk(1,1)) + + eval(['omc_kk = omc_' num2str(kk) ';']); + eval(['Tc_kk = Tc_' num2str(kk) ';']); + N_kk = size(XX_kk,2); + + if ~exist(['n_sq_x_' num2str(kk)]), + no_grid = 1; + else + eval(['n_sq_x = n_sq_x_' num2str(kk) ';']); + if isnan(n_sq_x(1)), + no_grid = 1; + end; + end; + + + if ~no_grid, + eval(['n_sq_x = n_sq_x_' num2str(kk) ';']); + eval(['n_sq_y = n_sq_y_' num2str(kk) ';']); + if (N_kk ~= ((n_sq_x+1)*(n_sq_y+1))), + no_grid = 1; + end; + end; + + if ~isnan(omc_kk(1,1)), + + R_kk = rodrigues(omc_kk); + + YY_kk = R_kk * XX_kk + Tc_kk * ones(1,length(XX_kk)); + + uu = [-dX;-dY;0]/2; + uu = R_kk * uu + Tc_kk; + + if ~no_grid, + YYx = zeros(n_sq_x+1,n_sq_y+1); + YYy = zeros(n_sq_x+1,n_sq_y+1); + YYz = zeros(n_sq_x+1,n_sq_y+1); + + YYx(:) = YY_kk(1,:); + YYy(:) = YY_kk(2,:); + YYz(:) = YY_kk(3,:); + + %keyboard; + + figure(4); + hhh= mesh(YYx,YYz,-YYy); + set(hhh,'edgecolor',colors(rem(kk-1,6)+1),'linewidth',1); %,'facecolor','none'); + %plot3(YY_kk(1,:),YY_kk(3,:),-YY_kk(2,:),['o' colors(rem(kk-1,6)+1)]); + text(uu(1),uu(3),-uu(2),num2str(kk),'fontsize',14,'color',colors(rem(kk-1,6)+1)); + else + + figure(4); + plot3(YY_kk(1,:),YY_kk(3,:),-YY_kk(2,:),['.' colors(rem(kk-1,6)+1)]); + text(uu(1),uu(3),-uu(2),num2str(kk),'fontsize',14,'color',colors(rem(kk-1,6)+1)); + + end; + + end; + + end; + + end; + + end; + +end; + +figure(4);rotate3d on; +axis('equal'); +title('Extrinsic parameters'); +%view(60,30); +view(a,b); +hold off; + +set(4,'Name','3D','NumberTitle','off'); + +%fprintf(1,'To generate the complete movie associated to the optimization loop, try: check_convergence;\n'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_grid.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_grid.m new file mode 100755 index 0000000..0cf9abe --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_grid.m @@ -0,0 +1,234 @@ +function [x,X,n_sq_x,n_sq_y,ind_orig,ind_x,ind_y] = extract_grid(I,wintx,winty,fc,cc,kc,dX,dY); + +map = gray(256); + +minI = min(I(:)); +maxI = max(I(:)); + +Id = 255*(I - minI)/(maxI - minI); + + figure(2); + image(Id); + colormap(map); + + + if nargin < 2, + + disp('Window size for corner finder (wintx and winty):'); + wintx = input('wintx ([] = 5) = '); + if isempty(wintx), wintx = 5; end; + wintx = round(wintx); + winty = input('winty ([] = 5) = '); + if isempty(winty), winty = 5; end; + winty = round(winty); + + fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + + end; + + + + title('Click on the four extreme corners of the rectangular pattern...'); + + disp('Click on the four extreme corners of the rectangular complete pattern...'); + + [x,y] = ginput3(4); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + if (x(2) > x(1)), + x4 = x(1);y4 = y(1); x3 = x(2); y3 = y(2); + else + x4 = x(2);y4 = y(2); x3 = x(1); y3 = y(1); + end; + if (x(3) > x(4)), + x2 = x(3);y2 = y(3); x1 = x(4); y1 = y(4); + else + x2 = x(4);y2 = y(4); x1 = x(3); y1 = y(3); + end; + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'g-'); + plot(x,y,'og'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','g','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','g','Fontsize',14); + hold off; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + if ~exist('dX')|~exist('dY'), + + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=30mm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=30mm) = ']); + if isempty(dX), dX = 30; end; + if isempty(dY), dY = 30; end; + + end; + + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planart collineation: (return the normalization matrice as well) + + [Homo,Hnorm,inv_Hnorm] = compute_homography ([a00 a10 a11 a01],[0 1 1 0;0 0 1 1;1 1 1 1]); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + if nargin < 6, + + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + + else + + xy_corners_undist = comp_distortion_oulu([(x' - cc(1))/fc(1);(y'-cc(2))/fc(1)],kc); + + xu = xy_corners_undist(1,:)'; + yu = xy_corners_undist(2,:)'; + + [XXu] = projectedGrid ( [xu(1);yu(1)], [xu(2);yu(2)],[xu(3);yu(3)], [xu(4);yu(4)],n_sq_x+1,n_sq_y+1); % The full grid + + r2 = sum(XXu.^2); + XX = (ones(2,1)*(1 + kc(1) * r2 + kc(2) * (r2.^2))) .* XXu; + XX(1,:) = fc(1)*XX(1,:)+cc(1); + XX(2,:) = fc(2)*XX(2,:)+cc(2); + + end; + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + ind_x = (n_sq_x+1)*(n_sq_y + 1); + ind_y = 1; + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + image(Id); colormap(map); hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters.m new file mode 100755 index 0000000..035b97d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters.m @@ -0,0 +1,46 @@ + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +check_active_images; + +fc = solution(1:2);%*** +cc = solution(3:4);%*** +alpha_c = solution(5);%*** +kc = solution(6:9);%*** + + +% Calibration matrix: + +KK = [fc(1) fc(1)*alpha_c cc(1);0 fc(2) cc(2); 0 0 1]; +inv_KK = inv(KK); + +% Extract the extrinsic paramters, and recomputer the collineations + +for kk = 1:n_ima, + + if active_images(kk), + + omckk = solution(15+6*(kk-1) + 1:15+6*(kk-1) + 3);%*** + Tckk = solution(15+6*(kk-1) + 4:15+6*(kk-1) + 6);%*** + + Rckk = rodrigues(omckk); + + Hkk = KK * [Rckk(:,1) Rckk(:,2) Tckk]; + + Hkk = Hkk / Hkk(3,3); + + else + + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + Rckk = NaN*ones(3,3); + Hkk = NaN*ones(3,3); + + end; + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Rc_' num2str(kk) ' = Rckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + eval(['H_' num2str(kk) '= Hkk;']); + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters3D.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters3D.m new file mode 100755 index 0000000..841c6ab --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extract_parameters3D.m @@ -0,0 +1,36 @@ + +%%% Extraction of the final intrinsic and extrinsic paramaters: + + +fc = solution(1:2); +kc = solution(3:6); +cc = solution(6*n_ima + 4 +3:6*n_ima + 5 +3); + +% Calibration matrix: + +KK = [fc(1) 0 cc(1);0 fc(2) cc(2); 0 0 1]; +inv_KK = inv(KK); + +% Extract the extrinsic paramters, and recomputer the collineations + +for kk = 1:n_ima, + + omckk = solution(4+6*(kk-1) + 3:6*kk + 3); + + Tckk = solution(6*kk+1 + 3:6*kk+3 + 3); + + Rckk = rodrigues(omckk); + + Hlkk = KK * [Rckk(:,1) Rckk(:,2) Tckk]; + + Hlkk = Hlkk / Hlkk(3,3); + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Rc_' num2str(kk) ' = Rckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + + eval(['Hl_' num2str(kk) '=Hlkk;']); + +end; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extrinsic_computation.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extrinsic_computation.m new file mode 100755 index 0000000..fbba78e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/extrinsic_computation.m @@ -0,0 +1,185 @@ +%%% INPUT THE IMAGE FILE NAME: + +if ~exist('fc')|~exist('cc')|~exist('kc')|~exist('alpha_c'), + fprintf(1,'No intrinsic camera parameters available.\n'); + return; +end; + +dir; + +fprintf(1,'\n'); +disp('Computation of the extrinsic parameters from an image of a pattern'); +disp('The intrinsic camera parameters are assumed to be known (previously computed)'); + +fprintf(1,'\n'); +image_name = input('Image name (full name without extension): ','s'); + +format_image2 = '0'; + +while format_image2 == '0', + + format_image2 = input('Image format: ([]=''r''=''ras'', ''b''=''bmp'', ''t''=''tif'', ''p''=''pgm'', ''j''=''jpg'', ''m''=''ppm'') ','s'); + + if isempty(format_image2), + format_image2 = 'ras'; + end; + + if lower(format_image2(1)) == 'm', + format_image2 = 'ppm'; + else + if lower(format_image2(1)) == 'b', + format_image2 = 'bmp'; + else + if lower(format_image2(1)) == 't', + format_image2 = 'tif'; + else + if lower(format_image2(1)) == 'p', + format_image2 = 'pgm'; + else + if lower(format_image2(1)) == 'j', + format_image2 = 'jpg'; + else + if lower(format_image2(1)) == 'r', + format_image2 = 'ras'; + else + disp('Invalid image format'); + format_image2 = '0'; % Ask for format once again + end; + end; + end; + end; + end; + end; +end; + +ima_name = [image_name '.' format_image2]; + + +%%% READ IN IMAGE: + +if format_image2(1) == 'p', + if format_image2(2) == 'p', + I = double(loadppm(ima_name)); + else + I = double(loadpgm(ima_name)); + end; +else + if format_image2(1) == 'r', + I = readras(ima_name); + else + I = double(imread(ima_name)); + end; +end; + +if size(I,3)>1, + I = I(:,:,2); +end; + + +%%% EXTRACT GRID CORNERS: + +fprintf(1,'\nExtraction of the grid corners on the image\n'); + +disp('Window size for corner finder (wintx and winty):'); +wintx = input('wintx ([] = 5) = '); +if isempty(wintx), wintx = 5; end; +wintx = round(wintx); +winty = input('winty ([] = 5) = '); +if isempty(winty), winty = 5; end; +winty = round(winty); + +fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + +[x_ext,X_ext,n_sq_x,n_sq_y,ind_orig,ind_x,ind_y] = extract_grid(I,wintx,winty,fc,cc,kc); + + + +%%% Computation of the Extrinsic Parameters attached to the grid: + +[omc_ext,Tc_ext,Rc_ext,H_ext] = compute_extrinsic(x_ext,X_ext,fc,cc,kc,alpha_c); + + +%%% Reproject the points on the image: + +[x_reproj] = project_points2(X_ext,omc_ext,Tc_ext,fc,cc,kc,alpha_c); + +err_reproj = x_ext - x_reproj; + +err_std2 = std(err_reproj')'; + + +Basis = [X_ext(:,[ind_orig ind_x ind_orig ind_y ind_orig ])]; + +VX = Basis(:,2) - Basis(:,1); +VY = Basis(:,4) - Basis(:,1); + +nX = norm(VX); +nY = norm(VY); + +VZ = min(nX,nY) * cross(VX/nX,VY/nY); + +Basis = [Basis VZ]; + +[x_basis] = project_points2(Basis,omc_ext,Tc_ext,fc,cc,kc,alpha_c); + +dxpos = (x_basis(:,2) + x_basis(:,1))/2; +dypos = (x_basis(:,4) + x_basis(:,3))/2; +dzpos = (x_basis(:,6) + x_basis(:,5))/2; + + + +figure(2); +image(I); +colormap(gray(256)); +hold on; +plot(x_ext(1,:)+1,x_ext(2,:)+1,'r+'); +plot(x_reproj(1,:)+1,x_reproj(2,:)+1,'yo'); +h = text(x_ext(1,ind_orig)-25,x_ext(2,ind_orig)-25,'O'); +set(h,'Color','g','FontSize',14); +h2 = text(dxpos(1)+1,dxpos(2)-30,'X'); +set(h2,'Color','g','FontSize',14); +h3 = text(dypos(1)-30,dypos(2)+1,'Y'); +set(h3,'Color','g','FontSize',14); +h4 = text(dzpos(1)-10,dzpos(2)-20,'Z'); +set(h4,'Color','g','FontSize',14); +plot(x_basis(1,:)+1,x_basis(2,:)+1,'g-','linewidth',2); +title('Image points (+) and reprojected grid points (o)'); +hold off; + + +fprintf(1,'\n\nExtrinsic parameters:\n\n'); +fprintf(1,'Translation vector: Tc_ext = [ %3.6f \t %3.6f \t %3.6f ]\n',Tc_ext); +fprintf(1,'Rotation vector: omc_ext = [ %3.6f \t %3.6f \t %3.6f ]\n',omc_ext); +fprintf(1,'Rotation matrix: Rc_ext = [ %3.6f \t %3.6f \t %3.6f\n',Rc_ext(1,:)'); +fprintf(1,' %3.6f \t %3.6f \t %3.6f\n',Rc_ext(2,:)'); +fprintf(1,' %3.6f \t %3.6f \t %3.6f ]\n',Rc_ext(3,:)'); +fprintf(1,'Pixel error: err = [ %3.5f \t %3.5f ]\n\n',err_std2); + + + + + +return; + + +% Stores the results: + +kk = 1; + +% Stores location of grid wrt camera: + +eval(['omc_' num2str(kk) ' = omc_ext;']); +eval(['Tc_' num2str(kk) ' = Tc_ext;']); + +% Stores the projected points: + +eval(['y_' num2str(kk) ' = x_reproj;']); +eval(['X_' num2str(kk) ' = X_ext;']); +eval(['x_' num2str(kk) ' = x_ext;']); + + +% Organize the points in a grid: + +eval(['n_sq_x_' num2str(kk) ' = n_sq_x;']); +eval(['n_sq_y_' num2str(kk) ' = n_sq_y;']); + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixallvariables.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixallvariables.m new file mode 100755 index 0000000..b5808f3 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixallvariables.m @@ -0,0 +1,19 @@ +% Code that clears all empty or NaN variables + +varlist = whos; + +if ~isempty(varlist), + + Nvar = size(varlist,1); + + for c_var = 1:Nvar, + + var2fix = varlist(c_var).name; + + fixvariable; + + end; + +end; + +clear varlist var2fix Nvar c_var \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixvariable.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixvariable.m new file mode 100755 index 0000000..2213431 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/fixvariable.m @@ -0,0 +1,18 @@ +% Code that clears an empty variable, or a NaN vsriable. +% Does not clear structures, or cells. + +if exist('var2fix'), + if eval(['exist(''' var2fix ''') == 1']), + if eval(['isempty(' var2fix ')']), + eval(['clear ' var2fix ]); + else + if eval(['~isstruct(' var2fix ')']), + if eval(['~iscell(' var2fix ')']), + if eval(['isnan(' var2fix '(1))']), + eval(['clear ' var2fix ]); + end; + end; + end; + end; + end; +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ginput3.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ginput3.m new file mode 100755 index 0000000..56fe496 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ginput3.m @@ -0,0 +1,216 @@ +function [out1,out2,out3] = ginput2(arg1) +%GINPUT Graphical input from mouse. +% [X,Y] = GINPUT(N) gets N points from the current axes and returns +% the X- and Y-coordinates in length N vectors X and Y. The cursor +% can be positioned using a mouse (or by using the Arrow Keys on some +% systems). Data points are entered by pressing a mouse button +% or any key on the keyboard except carriage return, which terminates +% the input before N points are entered. +% +% [X,Y] = GINPUT gathers an unlimited number of points until the +% return key is pressed. +% +% [X,Y,BUTTON] = GINPUT(N) returns a third result, BUTTON, that +% contains a vector of integers specifying which mouse button was +% used (1,2,3 from left) or ASCII numbers if a key on the keyboard +% was used. + +% Copyright (c) 1984-96 by The MathWorks, Inc. +% $Revision: 5.18 $ $Date: 1996/11/10 17:48:08 $ + +% Fixed version by Jean-Yves Bouguet to have a cross instead of 2 lines +% More visible for images + +P = NaN*ones(16,16); +P(1:15,1:15) = 2*ones(15,15); +P(2:14,2:14) = ones(13,13); +P(3:13,3:13) = NaN*ones(11,11); +P(6:10,6:10) = 2*ones(5,5); +P(7:9,7:9) = 1*ones(3,3); + +out1 = []; out2 = []; out3 = []; y = []; +c = computer; +if ~strcmp(c(1:2),'PC') & ~strcmp(c(1:2),'MA') + tp = get(0,'TerminalProtocol'); +else + tp = 'micro'; +end + +if ~strcmp(tp,'none') & ~strcmp(tp,'x') & ~strcmp(tp,'micro'), + if nargout == 1, + if nargin == 1, + eval('out1 = trmginput(arg1);'); + else + eval('out1 = trmginput;'); + end + elseif nargout == 2 | nargout == 0, + if nargin == 1, + eval('[out1,out2] = trmginput(arg1);'); + else + eval('[out1,out2] = trmginput;'); + end + if nargout == 0 + out1 = [ out1 out2 ]; + end + elseif nargout == 3, + if nargin == 1, + eval('[out1,out2,out3] = trmginput(arg1);'); + else + eval('[out1,out2,out3] = trmginput;'); + end + end +else + + fig = gcf; + figure(gcf); + + if nargin == 0 + how_many = -1; + b = []; + else + how_many = arg1; + b = []; + if isstr(how_many) ... + | size(how_many,1) ~= 1 | size(how_many,2) ~= 1 ... + | ~(fix(how_many) == how_many) ... + | how_many < 0 + error('Requires a positive integer.') + end + if how_many == 0 + ptr_fig = 0; + while(ptr_fig ~= fig) + ptr_fig = get(0,'PointerWindow'); + end + scrn_pt = get(0,'PointerLocation'); + loc = get(fig,'Position'); + pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)]; + out1 = pt(1); y = pt(2); + elseif how_many < 0 + error('Argument must be a positive integer.') + end + end + +pointer = get(gcf,'pointer'); + +set(gcf,'Pointer','custom','PointerShapeCData',P,'PointerShapeHotSpot',[8,8]); +%set(gcf,'pointer','crosshair'); + fig_units = get(fig,'units'); + char = 0; + + while how_many ~= 0 + % Use no-side effect WAITFORBUTTONPRESS + waserr = 0; + eval('keydown = wfbp;', 'waserr = 1;'); + if(waserr == 1) + if(ishandle(fig)) + set(fig,'pointer',pointer,'units',fig_units); + error('Interrupted'); + else + error('Interrupted by figure deletion'); + end + end + + ptr_fig = get(0,'CurrentFigure'); + if(ptr_fig == fig) + if keydown + char = get(fig, 'CurrentCharacter'); + button = abs(get(fig, 'CurrentCharacter')); + scrn_pt = get(0, 'PointerLocation'); + set(fig,'units','pixels') + loc = get(fig, 'Position'); + pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)]; + set(fig,'CurrentPoint',pt); + else + button = get(fig, 'SelectionType'); + if strcmp(button,'open') + button = b(length(b)); + elseif strcmp(button,'normal') + button = 1; + elseif strcmp(button,'extend') + button = 2; + elseif strcmp(button,'alt') + button = 3; + else + error('Invalid mouse selection.') + end + end + pt = get(gca, 'CurrentPoint'); + + how_many = how_many - 1; + + if(char == 13) % & how_many ~= 0) + % if the return key was pressed, char will == 13, + % and that's our signal to break out of here whether + % or not we have collected all the requested data + % points. + % If this was an early breakout, don't include + % the key info in the return arrays. + % We will no longer count it if it's the last input. + break; + end + + out1 = [out1;pt(1,1)]; + y = [y;pt(1,2)]; + b = [b;button]; + end + end + + set(fig,'pointer',pointer,'units',fig_units); + + if nargout > 1 + out2 = y; + if nargout > 2 + out3 = b; + end + else + out1 = [out1 y]; + end + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function key = wfbp +%WFBP Replacement for WAITFORBUTTONPRESS that has no side effects. + +% Remove figure button functions +fprops = {'windowbuttonupfcn','buttondownfcn', ... + 'windowbuttondownfcn','windowbuttonmotionfcn'}; +fig = gcf; +fvals = get(fig,fprops); +set(fig,fprops,{'','','',''}) + +% Remove all other buttondown functions +ax = findobj(fig,'type','axes'); +if isempty(ax) + ch = {}; +else + ch = get(ax,{'Children'}); +end +for i=1:length(ch), + ch{i} = ch{i}(:)'; +end +h = [ax(:)',ch{:}]; +vals = get(h,{'buttondownfcn'}); +mt = repmat({''},size(vals)); +set(h,{'buttondownfcn'},mt); + +% Now wait for that buttonpress, and check for error conditions +waserr = 0; +eval(['if nargout==0,', ... + ' waitforbuttonpress,', ... + 'else,', ... + ' keydown = waitforbuttonpress;',... + 'end' ], 'waserr = 1;'); + +% Put everything back +if(ishandle(fig)) + set(fig,fprops,fvals) + set(h,{'buttondownfcn'},vals) +end + +if(waserr == 1) + error('Interrupted'); +end + +if nargout>0, key = keydown; end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim.m new file mode 100755 index 0000000..ad19f64 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim.m @@ -0,0 +1,139 @@ +%go_calib_optim +% +%Main calibration function. Computes the intrinsic andextrinsic parameters. +%Runs as a script. +% +%INPUT: x_1,x_2,x_3,...: Feature locations on the images +% X_1,X_2,X_3,...: Corresponding grid coordinates +% +%OUTPUT: fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% KK: The camera matrix (containing fc and cc) +% omc_1,omc_2,omc_3,...: 3D rotation vectors attached to the grid positions in space +% Tc_1,Tc_2,Tc_3,...: 3D translation vectors attached to the grid positions in space +% Rc_1,Rc_2,Rc_3,...: 3D rotation matrices corresponding to the omc vectors +% +%Method: Minimizes the pixel reprojection error in the least squares sense over the intrinsic +% camera parameters, and the extrinsic parameters (3D locations of the grids in space) +% +%Note: If the intrinsic camera parameters (fc, cc, kc) do not exist before, they are initialized through +% the function init_intrinsic_param.m. Otherwise, the variables in memory are used as initial guesses. +% +%Note: The row vector active_images consists of zeros and ones. To deactivate an image, set the +% corresponding entry in the active_images vector to zero. +% +%VERY IMPORTANT: This function works for 2D and 3D calibration rigs, except for init_intrinsic_param.m +%that is so far implemented to work only with 2D rigs. +%In the future, a more general function will be there. +%For now, if using a 3D calibration rig, set quick_init to 1 for an easy initialization of the focal length + + +if ~exist('n_ima'), + data_calib; % Load the images + click_calib; % Extract the corners +end; + + +check_active_images; + +check_extracted_images; + +check_active_images; + + +desactivated_images = []; + + +if ~exist('center_optim'), + center_optim = 1; %%% Set this variable to 0 if your do not want to estimate the principal point + %%% Required when using one image, and VERY RECOMMENDED WHEN USING LESS THAN 4 images +end; + +% Check 3D-ness of the calibration rig: +rig3D = 0; +for kk = ind_active, + eval(['X_kk = X_' num2str(kk) ';']); + if is3D(X_kk), + rig3D = 1; + end; +end; + + +if center_optim & (length(ind_active) < 2) & ~rig3D, + fprintf(1,'\nPrincipal point rejected from the optimization when using one image and planar rig (center_optim = 1).\n'); + center_optim = 0; %%% when using a single image, please, no principal point estimation!!! + est_alpha = 0; +end; + +if ~exist('dont_ask'), + dont_ask = 0; +end; + +if center_optim & (length(ind_active) < 5), + fprintf(1,'\nThe principal point estimation may be unreliable (using less than 5 images for calibration).\n'); + if ~dont_ask, + quest = input('Are you sure you want to keep the principal point in the optimization process? ([]=yes, other=no) '); + center_optim = isempty(quest); + end; +end; + +if center_optim, + fprintf(1,'\nINFO: To reject the principal point from the optimization, set center_optim = 0 in go_calib_optim.m\n'); +end; + +if ~exist('est_alpha'), + est_alpha = 0; % by default, do not estimate skew +end; + +if ~center_optim & (est_alpha), + fprintf(1,'WARNING: Since there is no principal point estimation, no skew estimation (est_alpha = 0)\n'); + est_alpha = 0; +else + if ~est_alpha, + fprintf(1,'WARNING: Skew not optimized. Check variable est_alpha.\n'); + alpha_c = 0; + else + fprintf(1,'WARNING: Skew is optimized. To disable skew estimation, set est_alpha=0.\n'); + end; +end; + + +if ~exist('est_dist'); + est_dist = [1;1;1;1]; +end; +if ~prod(est_dist), + fprintf(1,'\nWARNING: Distortion not fully estimated. Check variable est_dist.\n'); +end; + + + + +%%% MAIN OPTIMIZATION CALL!!!!! (look into this function for the details of implementation) +go_calib_optim_iter; + + + +if ~isempty(desactivated_images), + + param_list_save = param_list; + + fprintf(1,'\nNew optimization including the images that have been deactivated during the previous optimization.\n'); + active_images(desactivated_images) = ones(1,length(desactivated_images)); + desactivated_images = []; + + go_calib_optim_iter; + + if ~isempty(desactivated_images), + fprintf(1,['List of images left desactivated: ' num2str(desactivated_images) '\n' ] ); + end; + + param_list = [param_list_save(:,1:end-1) param_list]; + +end; + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +%graphout_calib; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim_iter.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim_iter.m new file mode 100755 index 0000000..e3d22f6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/go_calib_optim_iter.m @@ -0,0 +1,394 @@ +%go_calib_optim_iter +% +%Main calibration function. Computes the intrinsic andextrinsic parameters. +%Runs as a script. +% +%INPUT: x_1,x_2,x_3,...: Feature locations on the images +% X_1,X_2,X_3,...: Corresponding grid coordinates +% +%OUTPUT: fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% KK: The camera matrix (containing fc and cc) +% omc_1,omc_2,omc_3,...: 3D rotation vectors attached to the grid positions in space +% Tc_1,Tc_2,Tc_3,...: 3D translation vectors attached to the grid positions in space +% Rc_1,Rc_2,Rc_3,...: 3D rotation matrices corresponding to the omc vectors +% +%Method: Minimizes the pixel reprojection error in the least squares sense over the intrinsic +% camera parameters, and the extrinsic parameters (3D locations of the grids in space) +% +%Note: If the intrinsic camera parameters (fc, cc, kc) do not exist before, they are initialized through +% the function init_intrinsic_param.m. Otherwise, the variables in memory are used as initial guesses. +% +%Note: The row vector active_images consists of zeros and ones. To deactivate an image, set the +% corresponding entry in the active_images vector to zero. +% +%VERY IMPORTANT: This function works for 2D and 3D calibration rigs, except for init_intrinsic_param.m +%that is so far implemented to work only with 2D rigs. +%In the future, a more general function will be there. +%For now, if using a 3D calibration rig, quick_init is set to 1 for an easy initialization of the focal length + + +if ~exist('center_optim'), + center_optim = 1; %%% Set this variable to 0 if your do not want to estimate the principal point +end; + +if ~exist('est_dist'), + est_dist = [1;1;1;1]; +end; + +if ~exist('est_alpha'), + est_alpha = 0; % by default, do not estimate skew +end; + + +% Little fix in case of stupid values in the binary variables: +center_optim = ~~center_optim; +est_alpha = ~~est_alpha; +est_dist = ~~est_dist; + + +if ~exist('nx')&~exist('ny'), + fprintf(1,'WARNING: No image size (nx,ny) available. Setting nx=640 and ny=480\n'); + nx = 640; + ny = 480; +end; + + +check_active_images; + + +quick_init = 0; % Set to 1 for using a quick init (necessary when using 3D rigs) + + +if ~center_optim, % In the case where the principal point is not estimated, keep it at the center of the image + fprintf(1,'Principal point not optimized (center_optim=0). It is kept at the center of the image.\n'); + cc = [(nx-1)/2;(ny-1)/2]; +end; + + +if ~prod(est_dist), + fprintf(1,'\nDistortion not fully estimated. Check variable est_dist.\n'); +end; + +if ~est_alpha, + fprintf(1,'Skew not optimized (est_alpha=0).\n'); + alpha_c = 0; +end; + + +% Check 3D-ness of the calibration rig: +rig3D = 0; +for kk = ind_active, + eval(['X_kk = X_' num2str(kk) ';']); + if is3D(X_kk), + rig3D = 1; + end; +end; + +% If the rig is 3D, then no choice: the only valid initialization is manual! +if rig3D, + quick_init = 1; +end; + + + +alpha_smooth = 1; % set alpha_smooth = 1; for steepest gradient descent + + +% Conditioning threshold for view rejection +thresh_cond = 1e6; + + + +%% Initialization of the intrinsic parameters (if necessary) + +if ~exist('cc'), + fprintf(1,'Initialization of the principal point at the center of the image.\n'); + cc = [(nx-1)/2;(ny-1)/2]; + alpha_smooth = 0.4; % slow convergence +end; + + +if ~exist('kc'), + fprintf(1,'Initialization of the image distortion to zero.\n'); + kc = zeros(4,1); + alpha_smooth = 0.4; % slow convergence +end; + +if ~exist('alpha_c'), + fprintf(1,'Initialization of the image skew to zero.\n'); + alpha_c = 0; + alpha_smooth = 0.4; % slow convergence +end; + +if ~exist('fc')& quick_init, + FOV_angle = 35; % Initial camera field of view in degrees + fprintf(1,['Initialization of the focal length to a FOV of ' num2str(FOV_angle) ' degrees.\n']); + fc = (nx/2)/tan(pi*FOV_angle/360) * ones(2,1); + alpha_smooth = 0.4; % slow +end; + + +if ~exist('fc'), + % Initialization of the intrinsic parameters: + fprintf(1,'Initialization of the intrinsic parameters using the vanishing points of planar patterns.\n') + init_intrinsic_param; % The right way to go (if quick_init is not active)! + alpha_smooth = 0.4; % slow convergence +end; + + +if ~prod(est_dist), + % If no distortion estimated, set to zero the variables that are not estimated + kc = kc .* est_dist; +end; + + + + + +%% Initialization of the extrinsic parameters for global minimization: + + +init_param = [fc;cc;alpha_c;kc;zeros(6,1)]; + + + +for kk = 1:n_ima, + + if exist(['x_' num2str(kk)]), + + eval(['x_kk = x_' num2str(kk) ';']); + eval(['X_kk = X_' num2str(kk) ';']); + + if (isnan(x_kk(1,1))), + if active_images(kk), + fprintf(1,'Warning: Cannot calibrate with image %d. Need to extract grid corners first.\n',kk) + fprintf(1,' Set active_images(%d)=1; and run Extract grid corners.\n',kk) + end; + active_images(kk) = 0; + end; + if active_images(kk), + [omckk,Tckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc,alpha_c); + [omckk,Tckk,Rckk,JJ_kk] = compute_extrinsic_refine(omckk,Tckk,x_kk,X_kk,fc,cc,kc,alpha_c,20,thresh_cond); + if cond(JJ_kk)> thresh_cond, + active_images(kk) = 0; + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + fprintf(1,'\nWarning: View #%d ill-conditioned. This image is now set inactive.\n',kk) + desactivated_images = [desactivated_images kk]; + end; + if isnan(omckk(1,1)), + %fprintf(1,'\nWarning: Desactivating image %d. Re-activate it later by typing:\nactive_images(%d)=1;\nand re-run optimization\n',[kk kk]) + active_images(kk) = 0; + end; + else + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + end; + + else + + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + + if active_images(kk), + fprintf(1,'Warning: Cannot calibrate with image %d. Need to extract grid corners first.\n',kk) + fprintf(1,' Set active_images(%d)=1; and run Extract grid corners.\n',kk) + end; + + active_images(kk) = 0; + + end; + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + + init_param = [init_param; omckk ; Tckk]; + +end; + + +check_active_images; + + + +%-------------------- Main Optimization: + +fprintf(1,'\nMain calibration optimization procedure - Number of images: %d\n',length(ind_active)); + + +param = init_param; +change = 1; + +iter = 0; + +fprintf(1,'Gradient descent iterations: '); + +param_list = param; + +MaxIter = 30; + + +while (change > 1e-6)&(iter < MaxIter), + + fprintf(1,'%d...',iter+1); + + + %% The first step consists of updating the whole vector of knowns (intrinsic + extrinsic of active + %% images) through a one step steepest gradient descent. + + JJ = []; + ex = []; + + f = param(1:2); + c = param(3:4); + alpha = param(5); + k = param(6:9); + + + for kk = 1:n_ima, + + if active_images(kk), + + %omckk = param(4+6*(kk-1) + 3:6*kk + 3); + + %Tckk = param(6*kk+1 + 3:6*kk+3 + 3); + + omckk = param(15+6*(kk-1) + 1:15+6*(kk-1) + 3); + + Tckk = param(15+6*(kk-1) + 4:15+6*(kk-1) + 6); + + if isnan(omckk(1)), + fprintf(1,'Intrinsic parameters at frame %d do not exist\n',kk); + return; + end; + + eval(['X_kk = X_' num2str(kk) ';']); + eval(['x_kk = x_' num2str(kk) ';']); + + Np = size(X_kk,2); + + JJkk = zeros(2*Np,n_ima * 6 + 15); + + [x,dxdom,dxdT,dxdf,dxdc,dxdk,dxdalpha] = project_points2(X_kk,omckk,Tckk,f,c,k,alpha); + + exkk = x_kk - x; + + ex = [ex;exkk(:)]; + + JJkk(:,1:2) = dxdf; + JJkk(:,3:4) = dxdc; + JJkk(:,5) = dxdalpha; + JJkk(:,6:9) = dxdk; + JJkk(:,15+6*(kk-1) + 1:15+6*(kk-1) + 3) = dxdom; + JJkk(:,15+6*(kk-1) + 4:15+6*(kk-1) + 6) = dxdT; + + + + JJ = [JJ;JJkk]; + + + % Check if this view is ill-conditioned: + JJ_kk = [dxdom dxdT]; + if cond(JJ_kk)> thresh_cond, + active_images(kk) = 0; + fprintf(1,'\nWarning: View #%d ill-conditioned. This image is now set inactive.\n',kk) + desactivated_images = [desactivated_images kk]; + param(15+6*(kk-1) + 1:15+6*(kk-1) + 6) = NaN*ones(6,1); + end; + + + end; + + end; + + + % List of active images (necessary if changed): + check_active_images; + + + % The following vector helps to select the variables to update (for only active images): + + ind_Jac = find([ones(2,1);center_optim*ones(2,1);est_alpha;est_dist;zeros(6,1);reshape(ones(6,1)*active_images,6*n_ima,1)])'; + + + JJ = JJ(:,ind_Jac); + + JJ2 = JJ'*JJ; + + + % Smoothing coefficient: + + alpha_smooth2 = 1-(1-alpha_smooth)^(iter+1); %set to 1 to undo any smoothing! + + + param_innov = alpha_smooth2*inv(JJ2)*(JJ')*ex; + param_up = param(ind_Jac) + param_innov; + param(ind_Jac) = param_up; + + + % New intrinsic parameters: + + fc_current = param(1:2); + cc_current = param(3:4); + alpha_current = param(5); + kc_current = param(6:9); + + + % Change on the intrinsic parameters: + change = norm([fc_current;cc_current] - [f;c])/norm([fc_current;cc_current]); + + + %% Second step: (optional) - It makes convergence faster, and the region of convergence LARGER!!! + %% Recompute the extrinsic parameters only using compute_extrinsic.m (this may be useful sometimes) + %% The complete gradient descent method is useful to precisely update the intrinsic parameters. + + MaxIter2 = 20; + + + for kk = 1:n_ima, + if active_images(kk), + omc_current = param(15+6*(kk-1) + 1:15+6*(kk-1) + 3); + Tc_current = param(15+6*(kk-1) + 4:15+6*(kk-1) + 6); + eval(['X_kk = X_' num2str(kk) ';']); + eval(['x_kk = x_' num2str(kk) ';']); + [omc_current,Tc_current] = compute_extrinsic_init(x_kk,X_kk,fc_current,cc_current,kc_current,alpha_current); + [omckk,Tckk,Rckk,JJ_kk] = compute_extrinsic_refine(omc_current,Tc_current,x_kk,X_kk,fc_current,cc_current,kc_current,alpha_current,MaxIter2,thresh_cond); + if cond(JJ_kk)> thresh_cond, + active_images(kk) = 0; + fprintf(1,'\nWarning: View #%d ill-conditioned. This image is now set inactive.\n',kk) + desactivated_images = [desactivated_images kk]; + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + end; + param(15+6*(kk-1) + 1:15+6*(kk-1) + 3) = omckk; + param(15+6*(kk-1) + 4:15+6*(kk-1) + 6) = Tckk; + end; + end; + + param_list = [param_list param]; + + iter = iter + 1; + +end; + +fprintf(1,'\n'); + + +solution = param; + + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters; + +comp_error_calib; + +fprintf(1,'\n\nCalibration results after optimization:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f ]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f ]\n',cc); +fprintf(1,'Skew: alpha_c = [ %3.5f ] => angle of pixel = %3.5f degrees\n',alpha_c,90 - atan(alpha_c)*180/pi); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f ]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f ]\n\n',err_std); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ima_read_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ima_read_calib.m new file mode 100755 index 0000000..dbbc4e0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/ima_read_calib.m @@ -0,0 +1,158 @@ + +if ~exist('calib_name')|~exist('format_image'), + data_calib; + return; +end; + +check_directory; + +if ~exist('n_ima'), + data_calib; + return; +end; + +check_active_images; + + +images_read = active_images; + + +if exist('image_numbers'), + first_num = image_numbers(1); +end; + + +% Just to fix a minor bug: +if ~exist('first_num'), + first_num = image_numbers(1); +end; + + +image_numbers = first_num:n_ima-1+first_num; + +no_image_file = 0; + +i = 1; + +while (i <= n_ima), % & (~no_image_file), + + if active_images(i), + + %fprintf(1,'Loading image %d...\n',i); + + if ~type_numbering, + number_ext = num2str(image_numbers(i)); + else + number_ext = sprintf(['%.' num2str(N_slots) 'd'],image_numbers(i)); + end; + + ima_name = [calib_name number_ext '.' format_image]; + + if i == ind_active(1), + fprintf(1,'Loading image '); + end; + + if exist(ima_name), + + fprintf(1,'%d...',i); + + if format_image(1) == 'p', + if format_image(2) == 'p', + Ii = double(loadppm(ima_name)); + else + Ii = double(loadpgm(ima_name)); + end; + else + if format_image(1) == 'r', + Ii = readras(ima_name); + else + Ii = double(imread(ima_name)); + end; + end; + + + if size(Ii,3)>1, + Ii = Ii(:,:,2); + end; + + eval(['I_' num2str(i) ' = Ii;']); + + else + + fprintf(1,'%d...no image...',i); + + images_read(i) = 0; + + %no_image_file = 1; + + end; + + end; + + i = i+1; + +end; + + +ind_read = find(images_read); + + + + +if isempty(ind_read), + + fprintf(1,'\nWARNING! No image were read\n'); + + no_image_file = 1; + + +else + + + %fprintf(1,'\nWARNING! Every exsisting image in the directory is set active.\n'); + + + if no_image_file, + + %fprintf(1,'WARNING! Some images were not read properly\n'); + + end; + + + fprintf(1,'\n'); + + if size(I_1,1)~=480, + small_calib_image = 1; + else + small_calib_image = 0; + end; + + [Hcal,Wcal] = size(I_1); % size of the calibration image + + [ny,nx] = size(I_1); + + clickname = []; + + map = gray(256); + + %string_save = 'save calib_data n_ima type_numbering N_slots image_numbers format_image calib_name Hcal Wcal nx ny map small_calib_image'; + + %eval(string_save); + + disp('done'); + %click_calib; + +end; + +if ~exist('map'), map = gray(256); end; + +active_images = images_read; + +% Show all the calibration images: + + +if ~isempty(ind_read), + + mosaic; + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/init_intrinsic_param.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/init_intrinsic_param.m new file mode 100755 index 0000000..94a5240 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/init_intrinsic_param.m @@ -0,0 +1,158 @@ +%init_intrinsic_param +% +%Initialization of the intrinsic parameters. +%Runs as a script. +% +%INPUT: x_1,x_2,x_3,...: Feature locations on the images +% X_1,X_2,X_3,...: Corresponding grid coordinates +% +%OUTPUT: fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% alpha_c: skew coefficient +% KK: The camera matrix (containing fc, cc and alpha_c) +% +%Method: Computes the planar homographies H_1, H_2, H_3, ... and computes +% the focal length fc from orthogonal vanishing points constraint. +% The principal point cc is assumed at the center of the image. +% Assumes no image distortion (kc = [0;0;0;0]) +% +%Note: The row vector active_images consists of zeros and ones. To deactivate an image, set the +% corresponding entry in the active_images vector to zero. +% +% +%Important function called within that program: +% +%compute_homography.m: Computes the planar homography between points on the grid in 3D, and the image plane. +% +% +%VERY IMPORTANT: This function works only with 2D rigs. +%In the future, a more general function will be there (working with 3D rigs as well). + + + +check_active_images; + +if ~exist(['x_' num2str(ind_active(1)) ]), + click_calib; +end; + + +fprintf(1,'\nInitialization of the intrinsic parameters - Number of images: %d\n',length(ind_active)); + + +% Initialize the homographies: + +for kk = 1:n_ima, + eval(['x_kk = x_' num2str(kk) ';']); + eval(['X_kk = X_' num2str(kk) ';']); + if (isnan(x_kk(1,1))), + if active_images(kk), + fprintf(1,'WARNING: Cannot calibrate with image %d. Need to extract grid corners first.\n',kk) + fprintf(1,' Set active_images(%d)=1; and run Extract grid corners.\n',kk) + end; + active_images(kk) = 0; + end; + if active_images(kk), + eval(['H_' num2str(kk) ' = compute_homography(x_kk,X_kk(1:2,:));']); + else + eval(['H_' num2str(kk) ' = NaN*ones(3,3);']); + end; +end; + +check_active_images; + +% initial guess for principal point and distortion: + +if ~exist('nx'), [ny,nx] = size(I); end; + +c_init = [nx;ny]/2 - 0.5; % initialize at the center of the image +k_init = [0;0;0;0]; % initialize to zero (no distortion) + + + +% Compute explicitely the focal length using all the (mutually orthogonal) vanishing points +% note: The vanihing points are hidden in the planar collineations H_kk + +A = []; +b = []; + +% matrix that subtract the principal point: +Sub_cc = [1 0 -c_init(1);0 1 -c_init(2);0 0 1]; + +for kk=1:n_ima, + + if active_images(kk), + + eval(['Hkk = H_' num2str(kk) ';']); + + Hkk = Sub_cc * Hkk; + + % Extract vanishing points (direct and diagonals): + + V_hori_pix = Hkk(:,1); + V_vert_pix = Hkk(:,2); + V_diag1_pix = (Hkk(:,1)+Hkk(:,2))/2; + V_diag2_pix = (Hkk(:,1)-Hkk(:,2))/2; + + V_hori_pix = V_hori_pix/norm(V_hori_pix); + V_vert_pix = V_vert_pix/norm(V_vert_pix); + V_diag1_pix = V_diag1_pix/norm(V_diag1_pix); + V_diag2_pix = V_diag2_pix/norm(V_diag2_pix); + + a1 = V_hori_pix(1); + b1 = V_hori_pix(2); + c1 = V_hori_pix(3); + + a2 = V_vert_pix(1); + b2 = V_vert_pix(2); + c2 = V_vert_pix(3); + + a3 = V_diag1_pix(1); + b3 = V_diag1_pix(2); + c3 = V_diag1_pix(3); + + a4 = V_diag2_pix(1); + b4 = V_diag2_pix(2); + c4 = V_diag2_pix(3); + + A_kk = [a1*a2 b1*b2; + a3*a4 b3*b4]; + + b_kk = -[c1*c2;c3*c4]; + + + A = [A;A_kk]; + b = [b;b_kk]; + + end; + +end; + + +% use all the vanishing points to estimate focal length: + +f_init = sqrt(abs(1./(inv(A'*A)*A'*b))); % if using a two-focal model for initial guess + +alpha_init = 0; + +%f_init = sqrt(b'*(sum(A')') / (b'*b)) * ones(2,1); % if single focal length model is used + + +% Global calibration matrix (initial guess): + +KK = [f_init(1) alpha_init*f_init(1) c_init(1);0 f_init(2) c_init(2); 0 0 1]; +inv_KK = inv(KK); + + +cc = c_init; +fc = f_init; +kc = k_init; +alpha_c = alpha_init; + + +fprintf(1,'\n\nCalibration parameters after initialization:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f ]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f ]\n',cc); +fprintf(1,'Skew: alpha_c = [ %3.5f ] => angle of pixel = %3.5f degrees\n',alpha_c,90 - atan(alpha_c)*180/pi); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f ]\n',kc); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/is3D.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/is3D.m new file mode 100755 index 0000000..ab00b3d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/is3D.m @@ -0,0 +1,19 @@ +function test = is3D(X), + + +Np = size(X,2); + +%% Check for planarity of the structure: + +X_mean = mean(X')'; + +Y = X - (X_mean*ones(1,Np)); + +YY = Y*Y'; + +[U,S,V] = svd(YY); + +r = S(3,3)/S(2,2); + +test = (r > 1e-3); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loading_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loading_calib.m new file mode 100755 index 0000000..a0f50d2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loading_calib.m @@ -0,0 +1,10 @@ +if ~exist('Calib_Results.mat'), + fprintf(1,'\nCalibration file Calib_Results.mat not found!\n'); + return; +end; + +fprintf(1,'\nLoading calibration results from Calib_Results.mat\n'); + +load Calib_Results + +fprintf(1,'done\n'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadinr.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadinr.m new file mode 100755 index 0000000..91b6f89 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadinr.m @@ -0,0 +1,52 @@ +%LOADINR Load an INRIMAGE format file +% +% LOADINR(filename, im) +% +% Load an INRIA image format file and return it as a matrix +% +% SEE ALSO: saveinr +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1996 + +function im = loadinr(fname, im) + + fid = fopen(fname, 'r'); + + s = fgets(fid); + if strcmp(s(1:12), '#INRIMAGE-4#') == 0, + error('not INRIMAGE format'); + end + + % not very complete, only looks for the X/YDIM keys + while 1, + s = fgets(fid); + n = length(s) - 1; + if s(1) == '#', + break + end + if strcmp(s(1:5), 'XDIM='), + cols = str2num(s(6:n)); + end + if strcmp(s(1:5), 'YDIM='), + rows = str2num(s(6:n)); + end + if strcmp(s(1:4), 'CPU='), + if strcmp(s(5:n), 'sun') == 0, + error('not sun data ordering'); + end + end + + end + disp(['INRIMAGE format file ' num2str(rows) ' x ' num2str(cols)]) + + % now the binary data + fseek(fid, 256, 'bof'); + [im count] = fread(fid, [cols rows], 'float32'); + im = im'; + if count ~= (rows*cols), + error('file too short'); + end + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadpgm.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadpgm.m new file mode 100755 index 0000000..dfa8b61 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadpgm.m @@ -0,0 +1,89 @@ +%LOADPGM Load a PGM image +% +% I = loadpgm(filename) +% +% Returns a matrix containing the image loaded from the PGM format +% file filename. Handles ASCII (P2) and binary (P5) PGM file formats. +% +% If the filename has no extension, and open fails, a '.pgm' will +% be appended. +% +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function I = loadpgm(file) + white = [' ' 9 10 13]; % space, tab, lf, cr + white = setstr(white); + + fid = fopen(file, 'r'); + if fid < 0, + fid = fopen([file '.pgm'], 'r'); + end + if fid < 0, + error('Couldn''t open file'); + end + + magic = fread(fid, 2, 'char'); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + cols = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + rows = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + maxval = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + if magic(1) == 'P', + if magic(2) == '2', + %disp(['ASCII PGM file ' num2str(rows) ' x ' num2str(cols)]) + I = fscanf(fid, '%d', [cols rows])'; + elseif magic(2) == '5', + %disp(['Binary PGM file ' num2str(rows) ' x ' num2str(cols)]) + if maxval == 1, + fmt = 'unint1'; + elseif maxval == 15, + fmt = 'uint4'; + elseif maxval == 255, + fmt = 'uint8'; + elseif maxval == 2^32-1, + fmt = 'uint32'; + end + I = fread(fid, [cols rows], fmt)'; + else + disp('Not a PGM file'); + end + end + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadppm.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadppm.m new file mode 100755 index 0000000..0c004fc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/loadppm.m @@ -0,0 +1,109 @@ +%LOADPPM Load a PPM image +% +% I = loadppm(filename) +% +% Returns a matrix containing the image loaded from the PPM format +% file filename. Handles ASCII (P3) and binary (P6) PPM file formats. +% +% If the filename has no extension, and open fails, a '.ppm' and +% '.pnm' extension will be tried. +% +% SEE ALSO: saveppm loadpgm +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function I = loadppm(file) + white = [' ' 9 10 13]; % space, tab, lf, cr + white = setstr(white); + + fid = fopen(file, 'r'); + if fid < 0, + fid = fopen([file '.ppm'], 'r'); + end + if fid < 0, + fid = fopen([file '.pnm'], 'r'); + end + if fid < 0, + error('Couldn''t open file'); + end + + magic = fread(fid, 2, 'char'); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + cols = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + rows = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + maxval = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + if magic(1) == 'P', + if magic(2) == '3', + %disp(['ASCII PPM file ' num2str(rows) ' x ' num2str(cols)]) + I = fscanf(fid, '%d', [cols*3 rows]); + elseif magic(2) == '6', + %disp(['Binary PPM file ' num2str(rows) ' x ' num2str(cols)]) + if maxval == 1, + fmt = 'unint1'; + elseif maxval == 15, + fmt = 'uint4'; + elseif maxval == 255, + fmt = 'uint8'; + elseif maxval == 2^32-1, + fmt = 'uint32'; + end + I = fread(fid, [cols*3 rows], fmt); + else + disp('Not a PPM file'); + end + end + % + % now the matrix has interleaved columns of R, G, B + % + I = I'; + size(I); + R = I(:,1:3:(cols*3)); + G = I(:,2:3:(cols*3)); + B = I(:,3:3:(cols*3)); + fclose(fid); + + + I = zeros(rows,cols,3); + I(:,:,1) = R; + I(:,:,2) = G; + I(:,:,3) = B; + I = uint8(I); + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mean_std_robust.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mean_std_robust.m new file mode 100755 index 0000000..0d18a62 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mean_std_robust.m @@ -0,0 +1,7 @@ +function [m,s] = mean_std_robust(x); + +x = x(:); + +m = median(x); + +s = median(abs(x - m))*1.4836; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mosaic.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mosaic.m new file mode 100755 index 0000000..b056661 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/mosaic.m @@ -0,0 +1,92 @@ + +if ~exist('I_1'), + active_images_save = active_images; + ima_read_calib; + active_images = active_images_save; + check_active_images; +end; + +check_active_images; + +if isempty(ind_read), + return; +end; + + +n_col = floor(sqrt(n_ima*nx/ny)); + +n_row = ceil(n_ima / n_col); + + +ker2 = 1; +for ii = 1:n_col, + ker2 = conv(ker2,[1/4 1/2 1/4]); +end; + + +II = I_1(1:n_col:end,1:n_col:end); + +[ny2,nx2] = size(II); + + + +kk_c = 1; + +II_mosaic = []; + +for jj = 1:n_row, + + + II_row = []; + + for ii = 1:n_col, + + if (exist(['I_' num2str(kk_c)])) & (kk_c <= n_ima), + + if active_images(kk_c), + eval(['I = I_' num2str(kk_c) ';']); + %I = conv2(conv2(I,ker2,'same'),ker2','same'); % anti-aliasing + I = I(1:n_col:end,1:n_col:end); + else + I = zeros(ny2,nx2); + end; + + else + + I = zeros(ny2,nx2); + + end; + + + + II_row = [II_row I]; + + if ii ~= n_col, + + II_row = [II_row zeros(ny2,3)]; + + end; + + + kk_c = kk_c + 1; + + end; + + nn2 = size(II_row,2); + + if jj ~= n_row, + II_row = [II_row; zeros(3,nn2)]; + end; + + II_mosaic = [II_mosaic ; II_row]; + +end; + +figure(2); +image(II_mosaic); +colormap(gray(256)); +title('Calibration images'); +set(gca,'Xtick',[]) +set(gca,'Ytick',[]) +axis('image'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/normalize.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/normalize.m new file mode 100755 index 0000000..6dc7149 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/normalize.m @@ -0,0 +1,43 @@ +function [xn] = normalize(x_kk,fc,cc,kc,alpha_c), + +%normalize +% +%[xn] = normalize(x_kk,fc,cc,kc,alpha_c) +% +%Computes the normalized coordinates xn given the pixel coordinates x_kk +%and the intrinsic camera parameters fc, cc and kc. +% +%INPUT: x_kk: Feature locations on the images +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% alpha_c: Skew coefficient +% +%OUTPUT: xn: Normalized feature locations on the image plane (a 2XN matrix) +% +%Important functions called within that program: +% +%comp_distortion_oulu: undistort pixel coordinates. + +if nargin < 5, + alpha_c = 0; + if nargin < 4; + kc = [0;0;0;0]; + if nargin < 3; + cc = [0;0]; + if nargin < 2, + fc = [1;1]; + end; + end; + end; +end; + + +% First subtract principal point, and divide by the focal length: +temp = (x_kk(2,:) - cc(2))/fc(2); +x_distort = [(x_kk(1,:) - cc(1))/fc(1) - alpha_c*temp;temp]; + + +%Compensate for lens distortion: + +xn = comp_distortion_oulu(x_distort,kc); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/pgmread.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/pgmread.m new file mode 100755 index 0000000..c96ccb7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/pgmread.m @@ -0,0 +1,26 @@ +function img = pgmread(filename) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + +fid = fopen(filename,'r'); +fscanf(fid, 'P5\n'); +cmt = '#'; +while findstr(cmt, '#'), + cmt = fgets(fid); + if length(findstr(cmt, '#')) ~= 1, + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end +end + +%fgets(fid); + +%img = fscanf(fid,'%d',size); +%img = img'; + +img = fread(fid,[y,x],'uint8'); +img = img'; +fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project2_oulu.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project2_oulu.m new file mode 100755 index 0000000..c5c4a34 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project2_oulu.m @@ -0,0 +1,53 @@ +function [x] = project2_oulu(X,R,T,f,t,k) +%PROJECT Subsidiary to calib + +% (c) Pietro Perona -- March 24, 1994 +% California Institute of Technology +% Pasadena, CA +% +% Renamed because project exists in matlab 5.2!!! +% Now uses the more elaborate intrinsic model from Oulu + + + +[m,n] = size(X); + +Y = R*X + T*ones(1,n); +Z = Y(3,:); + +f = f(:); %% make a column vector +if length(f)==1, + f = [f f]'; +end; + +x = (Y(1:2,:) ./ (ones(2,1) * Z)) ; + + +radius_2 = x(1,:).^2 + x(2,:).^2; + +if length(k) > 1, + + radial_distortion = 1 + ones(2,1) * ((k(1) * radius_2) + (k(2) * radius_2.^2)); + + if length(k) < 4, + + delta_x = zeros(2,n); + + else + + delta_x = [2*k(3)*x(1,:).*x(2,:) + k(4)*(radius_2 + 2*x(1,:).^2) ; + k(3) * (radius_2 + 2*x(2,:).^2)+2*k(4)*x(1,:).*x(2,:)]; + + end; + + +else + + radial_distortion = 1 + ones(2,1) * ((k(1) * radius_2)); + + delta_x = zeros(2,n); + +end; + + +x = (x .* radial_distortion + delta_x).* (f * ones(1,n)) + t*ones(1,n); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points.m new file mode 100755 index 0000000..1823490 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points.m @@ -0,0 +1,276 @@ +function [xp,dxpdom,dxpdT,dxpdf,dxpdc,dxpdk] = project_points(X,om,T,f,c,k) + +%project_points.m +% +%[xp,dxpdom,dxpdT,dxpdf,dxpdc,dxpdk] = project_points(X,om,T,f,c,k) +% +%Projects a 3D structure onto the image plane. +% +%INPUT: X: 3D structure in the world coordinate frame (3xN matrix for N points) +% (om,T): Rigid motion parameters between world coordinate frame and camera reference frame +% om: rotation vector (3x1 vector); T: translation vector (3x1 vector) +% f: camera focal length in units of horizontal and vertical pixel units (2x1 vector) +% c: principal point location in pixel units (2x1 vector) +% k: Distortion coefficients (radial and tangential) (4x1 vector) +% +%OUTPUT: xp: Projected pixel coordinates (2xN matrix for N points) +% dxpdom: Derivative of xp with respect to om ((2N)x3 matrix) +% dxpdT: Derivative of xp with respect to T ((2N)x3 matrix) +% dxpdf: Derivative of xp with respect to f ((2N)x2 matrix) +% dxpdc: Derivative of xp with respect to c ((2N)x2 matrix) +% dxpdk: Derivative of xp with respect to k ((2N)x4 matrix) +% +%Definitions: +%Let P be a point in 3D of coordinates X in the world reference frame (stored in the matrix X) +%The coordinate vector of P in the camera reference frame is: Xc = R*X + T +%where R is the rotation matrix corresponding to the rotation vector om: R = rodrigues(om); +%call x, y and z the 3 coordinates of Xc: x = Xc(1); y = Xc(2); z = Xc(3); +%The pinehole projection coordinates of P is [a;b] where a=x/z and b=y/z. +%call r^2 = a^2 + b^2. +%The distorted point coordinates are: xd = [xx;yy] where: +% +%xx = a * (1 + kc(1)*r^2 + kc(2)*r^4) + 2*kc(3)*a*b + kc(4)*(r^2 + 2*a^2); +%yy = b * (1 + kc(1)*r^2 + kc(2)*r^4) + kc(3)*(r^2 + 2*b^2) + 2*kc(4)*a*b; +% +%The left terms correspond to radial distortion, the right terms correspond to tangential distortion +% +%Fianlly, convertion into pixel coordinates: The final pixel coordinates vector xp=[xxp;yyp] where: +% +%xxp = f(1)*xx + c(1) +%yyp = f(2)*yy + c(2) +% +% +%NOTE: About 90 percent of the code takes care fo computing the Jacobian matrices +% +% +%Important function called within that program: +% +%rodrigues.m: Computes the rotation matrix corresponding to a rotation vector +% +%rigid_motion.m: Computes the rigid motion transformation of a given structure + + + +if nargin < 6, + k = zeros(4,1); + if nargin < 5, + c = zeros(2,1); + if nargin < 4, + f = ones(2,1); + if nargin < 3, + T = zeros(3,1); + if nargin < 2, + om = zeros(3,1); + if nargin < 1, + error('Need at least a 3D structure to project (in project_points.m)'); + return; + end; + end; + end; + end; + end; +end; + + +[m,n] = size(X); + +[Y,dYdom,dYdT] = rigid_motion(X,om,T); + + +inv_Z = 1./Y(3,:); + +x = (Y(1:2,:) .* (ones(2,1) * inv_Z)) ; + + +bb = (-x(1,:) .* inv_Z)'*ones(1,3); +cc = (-x(2,:) .* inv_Z)'*ones(1,3); + + +dxdom = zeros(2*n,3); +dxdom(1:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdom(1:3:end,:) + bb .* dYdom(3:3:end,:); +dxdom(2:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdom(2:3:end,:) + cc .* dYdom(3:3:end,:); + +dxdT = zeros(2*n,3); +dxdT(1:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdT(1:3:end,:) + bb .* dYdT(3:3:end,:); +dxdT(2:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdT(2:3:end,:) + cc .* dYdT(3:3:end,:); + + +% Add distortion: + +r2 = x(1,:).^2 + x(2,:).^2; + + + +dr2dom = 2*((x(1,:)')*ones(1,3)) .* dxdom(1:2:end,:) + 2*((x(2,:)')*ones(1,3)) .* dxdom(2:2:end,:); +dr2dT = 2*((x(1,:)')*ones(1,3)) .* dxdT(1:2:end,:) + 2*((x(2,:)')*ones(1,3)) .* dxdT(2:2:end,:); + + +r4 = r2.^2; + +dr4dom = 2*((r2')*ones(1,3)) .* dr2dom; +dr4dT = 2*((r2')*ones(1,3)) .* dr2dT; + + +% Radial distortion: + +cdist = 1 + k(1) * r2 + k(2) * r4; + +dcdistdom = k(1) * dr2dom + k(2) * dr4dom; +dcdistdT = k(1) * dr2dT+ k(2) * dr4dT; +dcdistdk = [ r2' r4' zeros(n,2)]; + + +xd1 = x .* (ones(2,1)*cdist); + +dxd1dom = zeros(2*n,3); +dxd1dom(1:2:end,:) = (x(1,:)'*ones(1,3)) .* dcdistdom; +dxd1dom(2:2:end,:) = (x(2,:)'*ones(1,3)) .* dcdistdom; +coeff = (reshape([cdist;cdist],2*n,1)*ones(1,3)); +dxd1dom = dxd1dom + coeff.* dxdom; + +dxd1dT = zeros(2*n,3); +dxd1dT(1:2:end,:) = (x(1,:)'*ones(1,3)) .* dcdistdT; +dxd1dT(2:2:end,:) = (x(2,:)'*ones(1,3)) .* dcdistdT; +dxd1dT = dxd1dT + coeff.* dxdT; + +dxd1dk = zeros(2*n,4); +dxd1dk(1:2:end,:) = (x(1,:)'*ones(1,4)) .* dcdistdk; +dxd1dk(2:2:end,:) = (x(2,:)'*ones(1,4)) .* dcdistdk; + + + +% tangential distortion: + +a1 = 2.*x(1,:).*x(2,:); +a2 = r2 + 2*x(1,:).^2; +a3 = r2 + 2*x(2,:).^2; + +delta_x = [k(3)*a1 + k(4)*a2 ; + k(3) * a3 + k(4)*a1]; + + +ddelta_xdx = zeros(2*n,2*n); +aa = (2*k(3)*x(2,:)+6*k(4)*x(1,:))'*ones(1,3); +bb = (2*k(3)*x(1,:)+2*k(4)*x(2,:))'*ones(1,3); +cc = (6*k(3)*x(2,:)+2*k(4)*x(1,:))'*ones(1,3); + +ddelta_xdom = zeros(2*n,3); +ddelta_xdom(1:2:end,:) = aa .* dxdom(1:2:end,:) + bb .* dxdom(2:2:end,:); +ddelta_xdom(2:2:end,:) = bb .* dxdom(1:2:end,:) + cc .* dxdom(2:2:end,:); + +ddelta_xdT = zeros(2*n,3); +ddelta_xdT(1:2:end,:) = aa .* dxdT(1:2:end,:) + bb .* dxdT(2:2:end,:); +ddelta_xdT(2:2:end,:) = bb .* dxdT(1:2:end,:) + cc .* dxdT(2:2:end,:); + +ddelta_xdk = zeros(2*n,4); +ddelta_xdk(1:2:end,3) = a1'; +ddelta_xdk(1:2:end,4) = a2'; +ddelta_xdk(2:2:end,3) = a3'; +ddelta_xdk(2:2:end,4) = a1'; + + + +xd2 = xd1 + delta_x; + +dxd2dom = dxd1dom + ddelta_xdom ; +dxd2dT = dxd1dT + ddelta_xdT; +dxd2dk = dxd1dk + ddelta_xdk ; + + +% Pixel coordinates: + +xp = xd2 .* (f * ones(1,n)) + c*ones(1,n); + +coeff = reshape(f*ones(1,n),2*n,1); + +dxpdom = (coeff*ones(1,3)) .* dxd2dom; +dxpdT = (coeff*ones(1,3)) .* dxd2dT; +dxpdk = (coeff*ones(1,4)) .* dxd2dk; + +dxpdf = zeros(2*n,2); +dxpdf(1:2:end,1) = xd2(1,:)'; +dxpdf(2:2:end,2) = xd2(2,:)'; + +dxpdc = zeros(2*n,2); +dxpdc(1:2:end,1) = ones(n,1); +dxpdc(2:2:end,2) = ones(n,1); + + +return; + +% Test of the Jacobians: + +n = 10; + +X = 10*randn(3,n); +om = randn(3,1); +T = [10*randn(2,1);40]; +f = 1000*rand(2,1); +c = 1000*randn(2,1); +k = 0.5*randn(4,1); + + +[x,dxdom,dxdT,dxdf,dxdc,dxdk] = project_points(X,om,T,f,c,k); + + +% Test on om: NOT OK + +dom = 0.000000001 * norm(om)*randn(3,1); +om2 = om + dom; + +[x2] = project_points(X,om2,T,f,c,k); + +x_pred = x + reshape(dxdom * dom,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on T: OK!! + +dT = 0.0001 * norm(T)*randn(3,1); +T2 = T + dT; + +[x2] = project_points(X,om,T2,f,c,k); + +x_pred = x + reshape(dxdT * dT,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + + +% Test on f: OK!! + +df = 0.001 * norm(f)*randn(2,1); +f2 = f + df; + +[x2] = project_points(X,om,T,f2,c,k); + +x_pred = x + reshape(dxdf * df,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on c: OK!! + +dc = 0.01 * norm(c)*randn(2,1); +c2 = c + dc; + +[x2] = project_points(X,om,T,f,c2,k); + +x_pred = x + reshape(dxdc * dc,2,n); + +norm(x2-x)/norm(x2 - x_pred) + +% Test on k: OK!! + +dk = 0.001 * norm(4)*randn(4,1); +k2 = k + dk; + +[x2] = project_points(X,om,T,f,c,k2); + +x_pred = x + reshape(dxdk * dk,2,n); + +norm(x2-x)/norm(x2 - x_pred) diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points2.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points2.m new file mode 100755 index 0000000..5bb1b91 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/project_points2.m @@ -0,0 +1,312 @@ +function [xp,dxpdom,dxpdT,dxpdf,dxpdc,dxpdk,dxpdalpha] = project_points2(X,om,T,f,c,k,alpha) + +%project_points.m +% +%[xp,dxpdom,dxpdT,dxpdf,dxpdc,dxpdk] = project_points2(X,om,T,f,c,k,alpha) +% +%Projects a 3D structure onto the image plane. +% +%INPUT: X: 3D structure in the world coordinate frame (3xN matrix for N points) +% (om,T): Rigid motion parameters between world coordinate frame and camera reference frame +% om: rotation vector (3x1 vector); T: translation vector (3x1 vector) +% f: camera focal length in units of horizontal and vertical pixel units (2x1 vector) +% c: principal point location in pixel units (2x1 vector) +% k: Distortion coefficients (radial and tangential) (4x1 vector) +% alpha: Skew coefficient between x and y pixel (alpha = 0 <=> square pixels) +% +%OUTPUT: xp: Projected pixel coordinates (2xN matrix for N points) +% dxpdom: Derivative of xp with respect to om ((2N)x3 matrix) +% dxpdT: Derivative of xp with respect to T ((2N)x3 matrix) +% dxpdf: Derivative of xp with respect to f ((2N)x2 matrix) +% dxpdc: Derivative of xp with respect to c ((2N)x2 matrix) +% dxpdk: Derivative of xp with respect to k ((2N)x4 matrix) +% +%Definitions: +%Let P be a point in 3D of coordinates X in the world reference frame (stored in the matrix X) +%The coordinate vector of P in the camera reference frame is: Xc = R*X + T +%where R is the rotation matrix corresponding to the rotation vector om: R = rodrigues(om); +%call x, y and z the 3 coordinates of Xc: x = Xc(1); y = Xc(2); z = Xc(3); +%The pinehole projection coordinates of P is [a;b] where a=x/z and b=y/z. +%call r^2 = a^2 + b^2. +%The distorted point coordinates are: xd = [xx;yy] where: +% +%xx = a * (1 + kc(1)*r^2 + kc(2)*r^4) + 2*kc(3)*a*b + kc(4)*(r^2 + 2*a^2); +%yy = b * (1 + kc(1)*r^2 + kc(2)*r^4) + kc(3)*(r^2 + 2*b^2) + 2*kc(4)*a*b; +% +%The left terms correspond to radial distortion, the right terms correspond to tangential distortion +% +%Finally, convertion into pixel coordinates: The final pixel coordinates vector xp=[xxp;yyp] where: +% +%xxp = f(1)*(xx + alpha*yy) + c(1) +%yyp = f(2)*yy + c(2) +% +% +%NOTE: About 90 percent of the code takes care fo computing the Jacobian matrices +% +% +%Important function called within that program: +% +%rodrigues.m: Computes the rotation matrix corresponding to a rotation vector +% +%rigid_motion.m: Computes the rigid motion transformation of a given structure + + +if nargin < 7, + alpha = 0; + if nargin < 6, + k = zeros(4,1); + if nargin < 5, + c = zeros(2,1); + if nargin < 4, + f = ones(2,1); + if nargin < 3, + T = zeros(3,1); + if nargin < 2, + om = zeros(3,1); + if nargin < 1, + error('Need at least a 3D structure to project (in project_points.m)'); + return; + end; + end; + end; + end; + end; + end; +end; + + +[m,n] = size(X); + +[Y,dYdom,dYdT] = rigid_motion(X,om,T); + + +inv_Z = 1./Y(3,:); + +x = (Y(1:2,:) .* (ones(2,1) * inv_Z)) ; + + +bb = (-x(1,:) .* inv_Z)'*ones(1,3); +cc = (-x(2,:) .* inv_Z)'*ones(1,3); + + +dxdom = zeros(2*n,3); +dxdom(1:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdom(1:3:end,:) + bb .* dYdom(3:3:end,:); +dxdom(2:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdom(2:3:end,:) + cc .* dYdom(3:3:end,:); + +dxdT = zeros(2*n,3); +dxdT(1:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdT(1:3:end,:) + bb .* dYdT(3:3:end,:); +dxdT(2:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdT(2:3:end,:) + cc .* dYdT(3:3:end,:); + + +% Add distortion: + +r2 = x(1,:).^2 + x(2,:).^2; + + + +dr2dom = 2*((x(1,:)')*ones(1,3)) .* dxdom(1:2:end,:) + 2*((x(2,:)')*ones(1,3)) .* dxdom(2:2:end,:); +dr2dT = 2*((x(1,:)')*ones(1,3)) .* dxdT(1:2:end,:) + 2*((x(2,:)')*ones(1,3)) .* dxdT(2:2:end,:); + + +r4 = r2.^2; + +dr4dom = 2*((r2')*ones(1,3)) .* dr2dom; +dr4dT = 2*((r2')*ones(1,3)) .* dr2dT; + + +% Radial distortion: + +cdist = 1 + k(1) * r2 + k(2) * r4; + +dcdistdom = k(1) * dr2dom + k(2) * dr4dom; +dcdistdT = k(1) * dr2dT+ k(2) * dr4dT; +dcdistdk = [ r2' r4' zeros(n,2)]; + + +xd1 = x .* (ones(2,1)*cdist); + +dxd1dom = zeros(2*n,3); +dxd1dom(1:2:end,:) = (x(1,:)'*ones(1,3)) .* dcdistdom; +dxd1dom(2:2:end,:) = (x(2,:)'*ones(1,3)) .* dcdistdom; +coeff = (reshape([cdist;cdist],2*n,1)*ones(1,3)); +dxd1dom = dxd1dom + coeff.* dxdom; + +dxd1dT = zeros(2*n,3); +dxd1dT(1:2:end,:) = (x(1,:)'*ones(1,3)) .* dcdistdT; +dxd1dT(2:2:end,:) = (x(2,:)'*ones(1,3)) .* dcdistdT; +dxd1dT = dxd1dT + coeff.* dxdT; + +dxd1dk = zeros(2*n,4); +dxd1dk(1:2:end,:) = (x(1,:)'*ones(1,4)) .* dcdistdk; +dxd1dk(2:2:end,:) = (x(2,:)'*ones(1,4)) .* dcdistdk; + + + +% tangential distortion: + +a1 = 2.*x(1,:).*x(2,:); +a2 = r2 + 2*x(1,:).^2; +a3 = r2 + 2*x(2,:).^2; + +delta_x = [k(3)*a1 + k(4)*a2 ; + k(3) * a3 + k(4)*a1]; + + +ddelta_xdx = zeros(2*n,2*n); +aa = (2*k(3)*x(2,:)+6*k(4)*x(1,:))'*ones(1,3); +bb = (2*k(3)*x(1,:)+2*k(4)*x(2,:))'*ones(1,3); +cc = (6*k(3)*x(2,:)+2*k(4)*x(1,:))'*ones(1,3); + +ddelta_xdom = zeros(2*n,3); +ddelta_xdom(1:2:end,:) = aa .* dxdom(1:2:end,:) + bb .* dxdom(2:2:end,:); +ddelta_xdom(2:2:end,:) = bb .* dxdom(1:2:end,:) + cc .* dxdom(2:2:end,:); + +ddelta_xdT = zeros(2*n,3); +ddelta_xdT(1:2:end,:) = aa .* dxdT(1:2:end,:) + bb .* dxdT(2:2:end,:); +ddelta_xdT(2:2:end,:) = bb .* dxdT(1:2:end,:) + cc .* dxdT(2:2:end,:); + +ddelta_xdk = zeros(2*n,4); +ddelta_xdk(1:2:end,3) = a1'; +ddelta_xdk(1:2:end,4) = a2'; +ddelta_xdk(2:2:end,3) = a3'; +ddelta_xdk(2:2:end,4) = a1'; + + + +xd2 = xd1 + delta_x; + +dxd2dom = dxd1dom + ddelta_xdom ; +dxd2dT = dxd1dT + ddelta_xdT; +dxd2dk = dxd1dk + ddelta_xdk ; + + +% Add Skew: + +xd3 = [xd2(1,:) + alpha*xd2(2,:);xd2(2,:)]; + +% Compute: dxd3dom, dxd3dT, dxd3dk, dxd3dalpha + +dxd3dom = zeros(2*n,3); +dxd3dom(1:2:2*n,:) = dxd2dom(1:2:2*n,:) + alpha*dxd2dom(2:2:2*n,:); +dxd3dom(2:2:2*n,:) = dxd2dom(2:2:2*n,:); +dxd3dT = zeros(2*n,3); +dxd3dT(1:2:2*n,:) = dxd2dT(1:2:2*n,:) + alpha*dxd2dT(2:2:2*n,:); +dxd3dT(2:2:2*n,:) = dxd2dT(2:2:2*n,:); +dxd3dk = zeros(2*n,4); +dxd3dk(1:2:2*n,:) = dxd2dk(1:2:2*n,:) + alpha*dxd2dk(2:2:2*n,:); +dxd3dk(2:2:2*n,:) = dxd2dk(2:2:2*n,:); +dxd3dalpha = zeros(2*n,1); +dxd3dalpha(1:2:2*n,:) = xd2(2,:)'; + + + +% Pixel coordinates: + +xp = xd3 .* (f * ones(1,n)) + c*ones(1,n); + +coeff = reshape(f*ones(1,n),2*n,1); + +dxpdom = (coeff*ones(1,3)) .* dxd3dom; +dxpdT = (coeff*ones(1,3)) .* dxd3dT; +dxpdk = (coeff*ones(1,4)) .* dxd3dk; +dxpdalpha = (coeff) .* dxd3dalpha; + +dxpdf = zeros(2*n,2); +dxpdf(1:2:end,1) = xd2(1,:)'; +dxpdf(2:2:end,2) = xd2(2,:)'; + +dxpdc = zeros(2*n,2); +dxpdc(1:2:end,1) = ones(n,1); +dxpdc(2:2:end,2) = ones(n,1); + + +return; + +% Test of the Jacobians: + +n = 10; + +X = 10*randn(3,n); +om = randn(3,1); +T = [10*randn(2,1);40]; +f = 1000*rand(2,1); +c = 1000*randn(2,1); +k = 0.5*randn(4,1); +alpha = 0.01*randn(1,1); + +[x,dxdom,dxdT,dxdf,dxdc,dxdk,dxdalpha] = project_points2(X,om,T,f,c,k,alpha); + + +% Test on om: NOT OK + +dom = 0.000000001 * norm(om)*randn(3,1); +om2 = om + dom; + +[x2] = project_points2(X,om2,T,f,c,k,alpha); + +x_pred = x + reshape(dxdom * dom,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on T: OK!! + +dT = 0.0001 * norm(T)*randn(3,1); +T2 = T + dT; + +[x2] = project_points2(X,om,T2,f,c,k,alpha); + +x_pred = x + reshape(dxdT * dT,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + + +% Test on f: OK!! + +df = 0.001 * norm(f)*randn(2,1); +f2 = f + df; + +[x2] = project_points2(X,om,T,f2,c,k,alpha); + +x_pred = x + reshape(dxdf * df,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on c: OK!! + +dc = 0.01 * norm(c)*randn(2,1); +c2 = c + dc; + +[x2] = project_points2(X,om,T,f,c2,k,alpha); + +x_pred = x + reshape(dxdc * dc,2,n); + +norm(x2-x)/norm(x2 - x_pred) + +% Test on k: OK!! + +dk = 0.001 * norm(k)*randn(4,1); +k2 = k + dk; + +[x2] = project_points2(X,om,T,f,c,k2,alpha); + +x_pred = x + reshape(dxdk * dk,2,n); + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on alpha: OK!! + +dalpha = 0.001 * norm(k)*randn(1,1); +alpha2 = alpha + dalpha; + +[x2] = project_points2(X,om,T,f,c,k,alpha2); + +x_pred = x + reshape(dxdalpha * dalpha,2,n); + +norm(x2-x)/norm(x2 - x_pred) diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projectedGrid.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projectedGrid.m new file mode 100755 index 0000000..561a7d0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projectedGrid.m @@ -0,0 +1,24 @@ +function [XX,H] = projectedGrid ( P1, P2, P3, P4 , nx, ny); + +% new formalism using homographies + +a00 = [P1;1]; +a10 = [P2;1]; +a11 = [P3;1]; +a01 = [P4;1]; + +% Compute the planart collineation: + +[H] = compute_collineation (a00, a10, a11, a01); + + +% Build the grid using the planar collineation: + +x_l = ((0:(nx-1))'*ones(1,ny))/(nx-1); +y_l = (ones(nx,1)*(0:(ny-1)))/(ny-1); + +pts = [x_l(:) y_l(:) ones(nx*ny,1)]'; + +XX = H*pts; + +XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projector_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projector_calib.m new file mode 100755 index 0000000..bb4ef86 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/projector_calib.m @@ -0,0 +1,258 @@ +%%% This code is an additional code that helps doing projector calibration in 3D scanning setup. +%%% This is not a useful code for anyone else but me. +%%% I included it in the toolbox for illustration only. + + +load camera_results; + + +proj_name = input('Basename projector calibration images (without number nor suffix): ','s'); + + +i = 1; + +while (i <= n_ima), % & (~no_image_file), + + if active_images(i), + + %fprintf(1,'Loading image %d...\n',i); + + if ~type_numbering, + number_ext = num2str(image_numbers(i)); + else + number_ext = sprintf(['%.' num2str(N_slots) 'd'],image_numbers(i)); + end; + + ima_namep = [proj_name number_ext 'p.' format_image]; + ima_namen = [proj_name number_ext 'n.' format_image]; + + if i == ind_active(1), + fprintf(1,'Loading image '); + end; + + fprintf(1,'%d...',i); + + if format_image(1) == 'p', + if format_image(2) == 'p', + Ip = double(loadppm(ima_namep)); + In = double(loadppm(ima_namen)); + else + Ip = double(loadpgm(ima_namep)); + In = double(loadpgm(ima_namen)); + end; + else + if format_image(1) == 'r', + Ip = readras(ima_namep); + In = readras(ima_namen); + else + Ip = double(imread(ima_namep)); + In = double(imread(ima_namen)); + end; + end; + + + if size(Ip,3)>1, + Ip = Ip(:,:,2); + In = In(:,:,2); + end; + + eval(['Ip_' num2str(i) ' = Ip;']); + eval(['In_' num2str(i) ' = In;']); + + end; + + i = i+1; + +end; + + +fprintf(1,'\nExtraction of the grid corners on the image\n'); + +disp('Window size for corner finder (wintx and winty):'); +wintx = input('wintx ([] = 5) = '); +if isempty(wintx), wintx = 5; end; +wintx = round(wintx); +winty = input('winty ([] = 5) = '); +if isempty(winty), winty = 5; end; +winty = round(winty); +fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + + +disp('The projector you are using is the DLP or Intel'); +nx = 800; +ny = 600; + +dX = input('Size dX in x of the squares (in pixels) [50] = '); +dY = input('Size dY in y of the squares (in pixels) [50] = '); + +if isempty(dX), dX=50; end; +if isempty(dY), dY=50; end; + +dXoff = input('Position in x of your reference (in pixels) [399.5] = '); +dYoff = input('Position in y of your reference (in pixels) [299.5] = '); + +if isempty(dXoff), dXoff=399.5; end; +if isempty(dYoff), dYoff=299.5; end; + +end; + + + +for kk = ind_active, + + eval(['Ip = Ip_' num2str(kk) ';']); + eval(['In = In_' num2str(kk) ';']); + + [x,X,n_sq_x,n_sq_y,ind_orig,ind_x,ind_y] = extract_grid(In,wintx,winty,fc,cc,kc,dX,dY); + xproj = x; + + Np_proj = size(x,2); + + figure(2); + image(Ip); + hold on; + plot(xproj(1,:)+1,xproj(2,:)+1,'r+'); + title('Click on your reference point'); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + hold off; + + disp('Click on your reference point...'); + + [xr,yr] = ginput2(1); + + err = sqrt(sum((xproj - [xr;yr]*ones(1,Np_proj)).^2)); + ind_ref = find(err == min(err)); + + ref_pt = xproj(:,ind_ref); + + + figure(2); + hold on; + plot(ref_pt(1)+1,ref_pt(2)+1,'go'); hold off; + + + nn = floor(ind_ref/(n_sq_x+1)); + off_x = ind_ref - nn*(n_sq_x+1) - 1; + off_y = n_sq_y - nn; + + + xprojn = xproj - cc * ones(1,Np_proj); + xprojn = xprojn ./ (fc * ones(1,Np_proj)); + xprojn = comp_distortion(xprojn,kc); + + eval(['Rc = Rc_' num2str(kk) ';']); + eval(['Tc = Tc_' num2str(kk) ';']); + + Zc = ((Rc(:,3)'*Tc) * (1./(Rc(:,3)' * [xprojn; ones(1,Np_proj)]))); + Xcp = (ones(3,1)*Zc) .* [xprojn; ones(1,Np_proj)]; % % in the camera frame + %Xproj = Rc'* Xcp - (Rc'*Tc)*ones(1,Np); % in the object frame !!! it works! + %Xproj(3,:) = zeros(1,Np); + + eval(['X_proj_' num2str(kk) ' = Xcp;']); % coordinates of the points in the + + x_proj = X(1:2,:) + ([dXoff - dX * off_x ; dYoff - dY * off_y]*ones(1,Np_proj)); + + eval(['x_proj_' num2str(kk) ' = x_proj;']); % coordinates of the points in the + +end; + + + +X_proj = []; +x_proj = []; + +for kk = ind_active, + eval(['X_proj = [X_proj X_proj_' num2str(kk) '];']); + eval(['x_proj = [x_proj x_proj_' num2str(kk) '];']); +end; + + +%Save camera parameters: +fc_save = fc; +cc_save = cc; +kc_save = kc; + +omc_1_save = omc_1; +Rc_1_save = Rc_1; +Tc_1_save = Tc_1; + + +% Get started to calibrate projector: +clear fc cc kc + +n_ima = 1; +X_1 = X_proj; +x_1 = x_proj; + + +% Image size: (may or may not be available) + +nx = 800; +ny = 600; + +% No calibration image is available (only the corner coordinates) + +no_image = 1; + +% Set the toolbox not to prompt the user (choose default values) + +dont_ask = 1; + +% Do not estimate distortion: + +est_dist = [0;0;0;0]; +est_dist = ones(4,1); + +center_optim = 1; + +% Run the main calibration routine: + +go_calib_optim_iter; + +% Shows the extrinsic parameters: + +dX = 3; +dY = 3; + +ext_calib; + +% Reprojection on the original images: + +reproject_calib; + + + + +%----------------------- Retrieve results: + +% Intrinsic: + +% Projector: +fp = fc; +cp = cc; +kp = kc; + +% Camera: +fc = fc_save; +cc = cc_save; +kc = kc_save; + +% Extrinsic: + +% Relative position of projector and camera: +T = Tc_1; +om = omc_1; +R = rodrigues(om); + +% Relative prosition of camera wrt world: +omc = omc_1_save; +Rc = Rc_1_save; +Tc = Tc_1_save; + +% relative position of projector wrt world: +Rp = R*Rc; +omp = rodrigues(Rp); +Tp = T + R*Tc; + +eval(['save calib_cam_proj R om T fc fp cc cp kc kp Rc Rp Tc Tp']); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/readras.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/readras.m new file mode 100755 index 0000000..fc1820b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/readras.m @@ -0,0 +1,87 @@ +function [X, map] = readras(filename, ys, ye, xs, xe); +%READRAS Read an image file in sun raster format. +% READRAS('imagefile.ras') reads a "sun.raster" image file. +% [X, map] = READRAS('imagefile.ras') returns both the image and a +% color map, so that +% [X, map] = readras('imagefile.ras'); +% image(X) +% colormap(map) +% axis('equal') +% will display the result with the proper colors. +% NOTE: readras cannot deal with complicated color maps. +% In fact, Matlab doesn't quite allow to work with colormaps +% with more than 64 entries. +% + +%% +%% (C) Thomas K. Leung 3/30/93. +%% California Institute of Technology. +%% Modified by Andrea Mennucci to deal with color images +%% + +% PC and UNIX version of readras - Jean-Yves Bouguet - Dec. 1998 + +dot = max(find(filename == '.')); +suffix = filename(dot+1:dot+3); + +if(strcmp(suffix, 'ras')) % raster file format % + fp = fopen(filename, 'rb'); + if(fp<0) error(['Cannot open ' filename '.']), end + + %Read and crack the 32-byte header + fseek(fp, 4, -1); + + width = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + height = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + depth = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + length = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + type = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + maptype = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + maplen = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + maplen = maplen / 3; + + if maptype == 2 % RMT_RAW + map = fread(fp, [maplen, 3], 'uchar')/255; +% if maplen<64, map=[map',zeros(3,64-maplen)]';maplen=64; end; + elseif maptype == 1 % RMT_EQUAL_RGB + map(:,1) = fread(fp, [maplen], 'uchar'); + map(:,2) = fread(fp, [maplen], 'uchar'); + map(:,3) = fread(fp, [maplen], 'uchar'); + %maxmap = max(max(map)); + map = map/255; + if maplen<64, map=[map',zeros(3,64-maplen)]'; maplen=64; end; + else % RMT_NONE + map = []; + end +% if maplen>64, +% map=[map',zeros(3,256-maplen)]'; +% end; + + % Read the image + + if rem(width,2) == 1 + Xt = fread(fp, [width+1, height], 'uchar'); + X = Xt(1:width, :)'; + else + Xt = fread(fp, [width, height], 'uchar'); + X = Xt'; + end + X = X + 1; + fclose(fp); +else + error('Image file name must end in either ''ras'' or ''rast''.'); +end + + +if nargin == 5 + + X = X(ys:ye, xs:xe); + +end \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/recomp_corner_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/recomp_corner_calib.m new file mode 100755 index 0000000..0909c69 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/recomp_corner_calib.m @@ -0,0 +1,119 @@ +% Re-select te corners after calibration + +if ~exist('n_ima')|~exist('fc'), + fprintf(1,'No calibration data available.\n'); + return; +end; + +check_active_images; + +flag = 0; +for kk = ind_active, + if ~exist(['y_' num2str(kk)]), + flag = 1; + else + eval(['ykk = y_' num2str(kk) ';']); + if isnan(ykk(1,1)), + flag = 1; + end; + end; +end; + +if flag, + fprintf(1,'Need to calibrate once before before recomputing image corners. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + + +if ~exist(['I_' num2str(ind_active(1))]), + n_ima_save = n_ima; + active_images_save = active_images; + ima_read_calib; + n_ima = n_ima_save; + active_images = active_images_save; + check_active_images; + if no_image_file, + disp('Cannot extract corners without images'); + return; + end; +end; + +fprintf(1,'\nRe-extraction of the grid corners on the images (after first calibration)\n'); + +disp('Window size for corner finder (wintx and winty):'); +wintx = input('wintx ([] = 5) = '); +if isempty(wintx), wintx = 5; end; +wintx = round(wintx); +winty = input('winty ([] = 5) = '); +if isempty(winty), winty = 5; end; +winty = round(winty); + +fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + +ima_numbers = input('Number(s) of image(s) to process ([] = all images) = '); + +if isempty(ima_numbers), + ima_proc = 1:n_ima; +else + ima_proc = ima_numbers; +end; + +q_auto = input('Use the projection of 3D grid or manual click ([]=auto, other=manual): '); + +fprintf(1,'Processing image '); + +for kk = ima_proc; + + if active_images(kk), + + fprintf(1,'%d...',kk); + + if isempty(q_auto), + + eval(['I = I_' num2str(kk) ';']); + + eval(['y = y_' num2str(kk) ';']); + + xc = cornerfinder(y+1,I,winty,wintx); % the four corners + + eval(['wintx_' num2str(kk) ' = wintx;']); + eval(['winty_' num2str(kk) ' = winty;']); + + eval(['x_' num2str(kk) '= xc - 1;']); + + else + + fprintf(1,'\n'); + + click_ima_calib; + + end; + + else + + if ~exist(['omc_' num2str(kk)]), + + eval(['dX_' num2str(kk) ' = NaN;']); + eval(['dY_' num2str(kk) ' = NaN;']); + + eval(['wintx_' num2str(kk) ' = NaN;']); + eval(['winty_' num2str(kk) ' = NaN;']); + + eval(['x_' num2str(kk) ' = NaN*ones(2,1);']); + eval(['X_' num2str(kk) ' = NaN*ones(3,1);']); + + eval(['n_sq_x_' num2str(kk) ' = NaN;']); + eval(['n_sq_y_' num2str(kk) ' = NaN;']); + + end; + + end; + + +end; + +% Recompute the error: + +comp_error_calib; + +fprintf(1,'\ndone\n'); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rect.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rect.m new file mode 100755 index 0000000..ccac7a5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rect.m @@ -0,0 +1,127 @@ +function [Irec] = rect(I,R,f,c,k,alpha,KK_new); + + +if nargin < 5, + k = 0; + if nargin < 4, + c = [0;0]; + if nargin < 3, + f = [1;1]; + if nargin < 2, + R = eye(3); + if nargin < 1, + error('ERROR: Need an image to rectify'); + break; + end; + end; + end; + end; +end; + + +if nargin < 7, + if nargin < 6, + KK_new = [f(1) 0 c(1);0 f(2) c(2);0 0 1]; + else + KK_new = alpha; % the 6th argument is actually KK_new + end; + alpha = 0; +end; + + + +% Note: R is the motion of the points in space +% So: X2 = R*X where X: coord in the old reference frame, X2: coord in the new ref frame. + + +if ~exist('KK_new'), + KK_new = [f(1) alpha_c*fc(1) c(1);0 f(2) c(2);0 0 1]; +end; + + +[nr,nc] = size(I); + +Irec = 255*ones(nr,nc); + +[mx,my] = meshgrid(1:nc, 1:nr); +px = reshape(mx',nc*nr,1); +py = reshape(my',nc*nr,1); + +rays = inv(KK_new)*[(px - 1)';(py - 1)';ones(1,length(px))]; + + +% Rotation: (or affine transformation): + +rays2 = R'*rays; + +x = [rays2(1,:)./rays2(3,:);rays2(2,:)./rays2(3,:)]; + + +% Add distortion: + +k1 = k(1); +k2 = k(2); + +p1 = k(3); +p2 = k(4); + +r_2 = sum(x.^2); + +delta_x = [2*p1*x(1,:).*x(2,:) + p2*(r_2 + 2*x(1,:).^2) ; + p1 * (r_2 + 2*x(2,:).^2)+2*p2*x(1,:).*x(2,:)]; + +xd = (ones(2,1)*( 1 + k1 * r_2 + k2 * r_2.^2)) .* x + delta_x; + + +% Reconvert in pixels: + +px2 = f(1)*(xd(1,:)+alpha_c*xd(2,:))+c(1); +py2 = f(2)*xd(2,:)+c(2); + + +% Interpolate between the closest pixels: + +px_0 = floor(px2); +px_1 = px_0 + 1; +alpha_x = px2 - px_0; + +py_0 = floor(py2); +py_1 = py_0 + 1; +alpha_y = py2 - py_0; + +good_points = find((px_0 >= 0) & (px_1 <= (nc-1)) & (py_0 >= 0) & (py_1 <= (nr-1))); + +I_lu = I(px_0(good_points) * nr + py_0(good_points) + 1); +I_ru = I(px_1(good_points) * nr + py_0(good_points) + 1); +I_ld = I(px_0(good_points) * nr + py_1(good_points) + 1); +I_rd = I(px_1(good_points) * nr + py_1(good_points) + 1); + + +I_interp = (1 - alpha_y(good_points)).*((1 - alpha_x(good_points)).* I_lu + alpha_x(good_points) .* I_ru) + alpha_y(good_points) .* ((1 - alpha_x(good_points)).* I_ld + alpha_x(good_points) .* I_rd); + + +Irec((px(good_points)-1)*nr + py(good_points)) = I_interp; + + + +return; + + +% Convert in indices: + +fact = 3; + +[XX,YY]= meshgrid(1:nc,1:nr); +[XXi,YYi]= meshgrid(1:1/fact:nc,1:1/fact:nr); + +%tic; +Iinterp = interp2(XX,YY,I,XXi,YYi); +%toc + +[nri,nci] = size(Iinterp); + + +ind_col = round(fact*(f(1)*xd(1,:)+c(1)))+1; +ind_row = round(fact*(f(2)*xd(2,:)+c(2)))+1; + +good_points = find((ind_col >=1)&(ind_col<=nci)&(ind_row >=1)& (ind_row <=nri)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/reproject_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/reproject_calib.m new file mode 100755 index 0000000..d3ad3d2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/reproject_calib.m @@ -0,0 +1,121 @@ +%%%%%%%%%%%%%%%%%%%% REPROJECT ON THE IMAGES %%%%%%%%%%%%%%%%%%%%%%%% + +if ~exist('n_ima')|~exist('fc'), + fprintf(1,'No calibration data available.\n'); + return; +end; + +if ~exist('no_image'), + no_image = 0; +end; + +if ~exist('nx')&~exist('ny'), + fprintf(1,'WARNING: No image size (nx,ny) available. Setting nx=640 and ny=480\n'); + nx = 640; + ny = 480; +end; + + +check_active_images; + + +% Color code for each image: + +colors = 'brgkcm'; + +% Reproject the patterns on the images, and compute the pixel errors: + +% Reload the images if necessary + +if ~exist(['omc_' num2str(ind_active(1)) ]), + fprintf(1,'Need to calibrate before showing image reprojection. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + +if ~no_image, + if ~exist(['I_' num2str(ind_active(1)) ]'), + n_ima_save = n_ima; + active_images_save = active_images; + ima_read_calib; + n_ima = n_ima_save; + active_images = active_images_save; + check_active_images; + if no_image_file, + fprintf(1,'WARNING: Do not show the original images\n'); %return; + end; + end; +else + no_image_file = 1; +end; + + +if ~exist('dont_ask'), + dont_ask = 0; +end; + + +if ~dont_ask, + ima_numbers = input('Number(s) of image(s) to show ([] = all images) = '); +else + ima_numbers = []; +end; + + +if isempty(ima_numbers), + ima_proc = 1:n_ima; +else + ima_proc = ima_numbers; +end; + + +figure(5); +for kk = ima_proc, %1:n_ima, + if exist(['y_' num2str(kk)]), + if active_images(kk) & eval(['~isnan(y_' num2str(kk) '(1,1))']), + eval(['plot(ex_' num2str(kk) '(1,:),ex_' num2str(kk) '(2,:),''' colors(rem(kk-1,6)+1) '+'');']); + hold on; + end; + end; +end; +hold off; +axis('equal'); +title('Reprojection error (in pixel)'); +xlabel('x'); +ylabel('y'); +drawnow; + +set(5,'Name','error','NumberTitle','off'); + + + +for kk = ima_proc, + if exist(['y_' num2str(kk)]), + if active_images(kk) & eval(['~isnan(y_' num2str(kk) '(1,1))']), + + if exist(['I_' num2str(kk)]), + eval(['I = I_' num2str(kk) ';']); + else + I = 255*ones(ny,nx); + end; + + figure(5+kk); + image(I); hold on; + colormap(gray(256)); + title(['Image ' num2str(kk) ' - Image points (+) and reprojected grid points (o)']); + eval(['plot(x_' num2str(kk) '(1,:)+1,x_' num2str(kk) '(2,:)+1,''r+'');']); + eval(['plot(y_' num2str(kk) '(1,:)+1,y_' num2str(kk) '(2,:)+1,''' colors(rem(kk-1,6)+1) 'o'');']); + zoom on; + axis([1 nx 1 ny]); + hold off; + drawnow; + + set(5+kk,'Name',num2str(kk),'NumberTitle','off'); + + end; + end; +end; + + +err_std = std(ex')'; + +fprintf(1,'Pixel error: err = [%3.5f %3.5f] (all active images)\n\n',err_std); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rigid_motion.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rigid_motion.m new file mode 100755 index 0000000..473405c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rigid_motion.m @@ -0,0 +1,66 @@ +function [Y,dYdom,dYdT] = rigid_motion(X,om,T); + +%rigid_motion.m +% +%[Y,dYdom,dYdT] = rigid_motion(X,om,T) +% +%Computes the rigid motion transformation Y = R*X+T, where R = rodrigues(om). +% +%INPUT: X: 3D structure in the world coordinate frame (3xN matrix for N points) +% (om,T): Rigid motion parameters between world coordinate frame and camera reference frame +% om: rotation vector (3x1 vector); T: translation vector (3x1 vector) +% +%OUTPUT: Y: 3D coordinates of the structure points in the camera reference frame (3xN matrix for N points) +% dYdom: Derivative of Y with respect to om ((3N)x3 matrix) +% dYdT: Derivative of Y with respect to T ((3N)x3 matrix) +% +%Definitions: +%Let P be a point in 3D of coordinates X in the world reference frame (stored in the matrix X) +%The coordinate vector of P in the camera reference frame is: Y = R*X + T +%where R is the rotation matrix corresponding to the rotation vector om: R = rodrigues(om); +% +%Important function called within that program: +% +%rodrigues.m: Computes the rotation matrix corresponding to a rotation vector + + + +if nargin < 3, + T = zeros(3,1); + if nargin < 2, + om = zeros(3,1); + if nargin < 1, + error('Need at least a 3D structure as input (in rigid_motion.m)'); + return; + end; + end; +end; + + +[R,dRdom] = rodrigues(om); + +[m,n] = size(X); + +Y = R*X + T*ones(1,n); + +if nargout > 1, + + +dYdR = zeros(3*n,9); +dYdT = zeros(3*n,3); + +dYdR(1:3:end,1:3:end) = X'; +dYdR(2:3:end,2:3:end) = X'; +dYdR(3:3:end,3:3:end) = X'; + +dYdT(1:3:end,1) = ones(n,1); +dYdT(2:3:end,2) = ones(n,1); +dYdT(3:3:end,3) = ones(n,1); + +dYdom = dYdR * dRdom; + +end; + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rodrigues.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rodrigues.m new file mode 100755 index 0000000..9d55337 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rodrigues.m @@ -0,0 +1,217 @@ +function [out,dout]=rodrigues(in) + +% RODRIGUES Transform rotation matrix into rotation vector and viceversa. +% +% Sintax: [OUT]=RODRIGUES(IN) +% If IN is a 3x3 rotation matrix then OUT is the +% corresponding 3x1 rotation vector +% if IN is a rotation 3-vector then OUT is the +% corresponding 3x3 rotation matrix +% + +%% +%% Copyright (c) March 1993 -- Pietro Perona +%% California Institute of Technology +%% + +%% ALL CHECKED BY JEAN-YVES BOUGUET, October 1995. +%% FOR ALL JACOBIAN MATRICES !!! LOOK AT THE TEST AT THE END !! + +%% BUG when norm(om)=pi fixed -- April 6th, 1997; +%% Jean-Yves Bouguet + + +[m,n] = size(in); +%bigeps = 10e+4*eps; +bigeps = 10e+20*eps; + +if ((m==1) & (n==3)) | ((m==3) & (n==1)) %% it is a rotation vector + theta = norm(in); + if theta < eps + R = eye(3); + + %if nargout > 1, + + dRdin = [0 0 0; + 0 0 1; + 0 -1 0; + 0 0 -1; + 0 0 0; + 1 0 0; + 0 1 0; + -1 0 0; + 0 0 0]; + + %end; + + else + if n==length(in) in=in'; end; %% make it a column vec. if necess. + + %m3 = [in,theta] + + dm3din = [eye(3);in'/theta]; + + omega = in/theta; + + %m2 = [omega;theta] + + dm2dm3 = [eye(3)/theta -in/theta^2; zeros(1,3) 1]; + + alpha = cos(theta); + beta = sin(theta); + gamma = 1-cos(theta); + omegav=[[0 -omega(3) omega(2)];[omega(3) 0 -omega(1)];[-omega(2) omega(1) 0 ]]; + A = omega*omega'; + + %m1 = [alpha;beta;gamma;omegav;A]; + + dm1dm2 = zeros(21,4); + dm1dm2(1,4) = -sin(theta); + dm1dm2(2,4) = cos(theta); + dm1dm2(3,4) = sin(theta); + dm1dm2(4:12,1:3) = [0 0 0 0 0 1 0 -1 0; + 0 0 -1 0 0 0 1 0 0; + 0 1 0 -1 0 0 0 0 0]'; + + w1 = omega(1); + w2 = omega(2); + w3 = omega(3); + + dm1dm2(13:21,1) = [2*w1;w2;w3;w2;0;0;w3;0;0]; + dm1dm2(13: 21,2) = [0;w1;0;w1;2*w2;w3;0;w3;0]; + dm1dm2(13:21,3) = [0;0;w1;0;0;w2;w1;w2;2*w3]; + + R = eye(3)*alpha + omegav*beta + A*gamma; + + dRdm1 = zeros(9,21); + + dRdm1([1 5 9],1) = ones(3,1); + dRdm1(:,2) = omegav(:); + dRdm1(:,4:12) = beta*eye(9); + dRdm1(:,3) = A(:); + dRdm1(:,13:21) = gamma*eye(9); + + dRdin = dRdm1 * dm1dm2 * dm2dm3 * dm3din; + + + end; + out = R; + dout = dRdin; + + %% it is prob. a rot matr. + elseif ((m==n) & (m==3) & (norm(in' * in - eye(3)) < bigeps)... + & (abs(det(in)-1) < bigeps)) + R = in; + + + + tr = (trace(R)-1)/2; + dtrdR = [1 0 0 0 1 0 0 0 1]/2; + theta = real(acos(tr)); + + + if sin(theta) >= 1e-5, + + dthetadtr = -1/sqrt(1-tr^2); + + dthetadR = dthetadtr * dtrdR; + % var1 = [vth;theta]; + vth = 1/(2*sin(theta)); + dvthdtheta = -vth*cos(theta)/sin(theta); + dvar1dtheta = [dvthdtheta;1]; + + dvar1dR = dvar1dtheta * dthetadR; + + + om1 = [R(3,2)-R(2,3), R(1,3)-R(3,1), R(2,1)-R(1,2)]'; + + dom1dR = [0 0 0 0 0 1 0 -1 0; + 0 0 -1 0 0 0 1 0 0; + 0 1 0 -1 0 0 0 0 0]; + + % var = [om1;vth;theta]; + dvardR = [dom1dR;dvar1dR]; + + % var2 = [om;theta]; + om = vth*om1; + domdvar = [vth*eye(3) om1 zeros(3,1)]; + dthetadvar = [0 0 0 0 1]; + dvar2dvar = [domdvar;dthetadvar]; + + + out = om*theta; + domegadvar2 = [theta*eye(3) om]; + + dout = domegadvar2 * dvar2dvar * dvardR; + + + else + if tr > 0; % case norm(om)=0; + + out = [0 0 0]'; + + dout = [0 0 0 0 0 1/2 0 -1/2 0; + 0 0 -1/2 0 0 0 1/2 0 0; + 0 1/2 0 -1/2 0 0 0 0 0]; + else % case norm(om)=pi; %% fixed April 6th + + + out = theta * (sqrt((diag(R)+1)/2).*[1;2*(R(1,2:3)>=0)'-1]); + %keyboard; + + if nargout > 1, + fprintf(1,'WARNING!!!! Jacobian domdR undefined!!!\n'); + dout = NaN*ones(3,9); + end; + end; + end; + + else + error('Neither a rotation matrix nor a rotation vector were provided'); + end; + +return; + +%% test of the Jacobians: + +%%%% TEST OF dRdom: +om = randn(3,1); +dom = randn(3,1)/1000000; + +[R1,dR1] = rodrigues(om); +R2 = rodrigues(om+dom); + +R2a = R1 + reshape(dR1 * dom,3,3); + +gain = norm(R2 - R1)/norm(R2 - R2a) + +%%% TEST OF dOmdR: +om = randn(3,1); +R = rodrigues(om); +dom = randn(3,1)/10000; +dR = rodrigues(om+dom) - R; + +[omc,domdR] = rodrigues(R); +[om2] = rodrigues(R+dR); + +om_app = omc + domdR*dR(:); + +gain = norm(om2 - omc)/norm(om2 - om_app) + + +%%% OTHER BUG: (FIXED NOW!!!) + +omu = randn(3,1); +omu = omu/norm(omu) +om = pi*omu; +[R,dR]= rodrigues(om); +[om2] = rodrigues(R); +[om om2] + +%%% NORMAL OPERATION + +om = randn(3,1); +[R,dR]= rodrigues(om); +[om2] = rodrigues(R); +[om om2] + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rotation.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rotation.m new file mode 100755 index 0000000..87ee2fe --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/rotation.m @@ -0,0 +1,23 @@ +function [] = rotation(st); + +if nargin < 1, + st= 1; +end; + + +fig = gcf; + +ax = gca; + +vv = get(ax,'view'); + +az = vv(1); +el = vv(2); + +for azi = az:-abs(st):az-360, + + set(ax,'view',[azi el]); + figure(fig); + drawnow; + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/run_error_analysis.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/run_error_analysis.m new file mode 100755 index 0000000..095e17e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/run_error_analysis.m @@ -0,0 +1,65 @@ +%%% Program that launchs the complete + +for N_ima_active = 1:30, + + error_analysis; + +end; + + + +return; + + +f = []; +f_std = []; + +c = []; +c_std = []; + +k = []; +k_std = []; + +NN = 30; + +for rr = 1:NN, + + load(['Calib_Accuracies_' num2str(rr)]); + + [m1,s1] = mean_std_robust(fc_list(1,:)); + [m2,s2] = mean_std_robust(fc_list(2,:)); + + f = [f [m1;m2]]; + f_std = [f_std [s1;s2]]; + + [m1,s1] = mean_std_robust(cc_list(1,:)); + [m2,s2] = mean_std_robust(cc_list(2,:)); + + c = [c [m1;m2]]; + c_std = [c_std [s1;s2]]; + + [m1,s1] = mean_std_robust(kc_list(1,:)); + [m2,s2] = mean_std_robust(kc_list(2,:)); + [m3,s3] = mean_std_robust(kc_list(3,:)); + [m4,s4] = mean_std_robust(kc_list(4,:)); + + k = [k [m1;m2;m3;m4]]; + k_std = [k_std [s1;s2;s3;s4]]; + +end; + +figure(1); +errorbar([1:NN;1:NN]',f',f_std'); +figure(2); +errorbar([1:NN;1:NN]',c',c_std'); +figure(3); +errorbar([1:NN;1:NN;1:NN;1:NN]',k',k_std'); + +figure(4); +semilogy(f_std'); + +figure(5); +semilogy(c_std'); + +figure(6); +semilogy(k_std'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveinr.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveinr.m new file mode 100755 index 0000000..a176e39 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveinr.m @@ -0,0 +1,46 @@ +%SAVEINR Write an INRIMAGE format file +% +% SAVEINR(filename, im) +% +% Saves the specified image array in a INRIA image format file. +% +% SEE ALSO: loadinr +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + +% Peter Corke 1996 + +function saveinr(fname, im) + + fid = fopen(fname, 'w'); + [r,c] = size(im'); + + % build the header + hdr = []; + s = sprintf('#INRIMAGE-4#{\n'); + hdr = [hdr s]; + s = sprintf('XDIM=%d\n',c); + hdr = [hdr s]; + s = sprintf('YDIM=%d\n',r); + hdr = [hdr s]; + s = sprintf('ZDIM=1\n'); + hdr = [hdr s]; + s = sprintf('VDIM=1\n'); + hdr = [hdr s]; + s = sprintf('TYPE=float\n'); + hdr = [hdr s]; + s = sprintf('PIXSIZE=32\n'); + hdr = [hdr s]; + s = sprintf('SCALE=2**0\n'); + hdr = [hdr s]; + s = sprintf('CPU=sun\n#'); + hdr = [hdr s]; + + % make it 256 bytes long and write it + hdr256 = zeros(1,256); + hdr256(1:length(hdr)) = hdr; + fwrite(fid, hdr256, 'uchar'); + + % now the binary data + fwrite(fid, im', 'float32'); + fclose(fid) diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/savepgm.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/savepgm.m new file mode 100755 index 0000000..0cee75d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/savepgm.m @@ -0,0 +1,22 @@ +%SAVEPGM Write a PGM format file +% +% SAVEPGM(filename, im) +% +% Saves the specified image array in a binary (P5) format PGM image file. +% +% SEE ALSO: loadpgm +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function savepgm(fname, im) + + fid = fopen(fname, 'w'); + [r,c] = size(im'); + fprintf(fid, 'P5\n'); + fprintf(fid, '%d %d\n', r, c); + fprintf(fid, '255\n'); + fwrite(fid, im', 'uchar'); + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveppm.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveppm.m new file mode 100755 index 0000000..ece092b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saveppm.m @@ -0,0 +1,45 @@ +%SAVEPPM Write a PPM format file +% +% SAVEPPM(filename, I) +% +% Saves the specified red, green and blue planes in a binary (P6) +% format PPM image file. +% +% SEE ALSO: loadppm +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function saveppm(fname, I) + +I = double(I); + +if size(I,3) == 1, + R = I; + G = I; + B = I; +else + R = I(:,:,1); + G = I(:,:,2); + B = I(:,:,3); +end; + +%keyboard; + + fid = fopen(fname, 'w'); + [r,c] = size(R'); + fprintf(fid, 'P6\n'); + fprintf(fid, '%d %d\n', r, c); + fprintf(fid, '255\n'); + R = R'; + G = G'; + B = B'; + im = [R(:) G(:) B(:)]; + %im = reshape(im,r,c*3); + im = im'; + %im = im(:); + fwrite(fid, im, 'uchar'); + fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saving_calib.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saving_calib.m new file mode 100755 index 0000000..3f98a8b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/saving_calib.m @@ -0,0 +1,95 @@ + +if ~exist('n_ima')|~exist('fc'), + fprintf(1,'No calibration data available.\n'); + return; +end; + +check_active_images; + +if ~exist('solution_init'), solution_init = []; end; + +for kk = 1:n_ima, + if ~exist(['dX_' num2str(kk)]), eval(['dX_' num2str(kk) '= dX;']); end; + if ~exist(['dY_' num2str(kk)]), eval(['dY_' num2str(kk) '= dY;']); end; +end; + +if ~exist('param_list'), + param_list = solution; +end; + +if ~exist('wintx'), + wintx = []; + winty = []; +end; + +if ~exist('dX_default'), + dX_default = []; + dY_default = []; +end; + +if ~exist('alpha_c'), + alpha_c = 0; +end; + +for kk = 1:n_ima, + if ~exist(['y_' num2str(kk)]), + eval(['y_' num2str(kk) ' = NaN*ones(2,1);']); + end; + if ~exist(['n_sq_x_' num2str(kk)]), + eval(['n_sq_x_' num2str(kk) ' = NaN;']); + eval(['n_sq_y_' num2str(kk) ' = NaN;']); + end; + if ~exist(['wintx_' num2str(kk)]), + eval(['wintx_' num2str(kk) ' = NaN;']); + eval(['winty_' num2str(kk) ' = NaN;']); + end; +end; + +save_name = 'Calib_Results'; + +if exist([ save_name '.mat'])==2, + disp('WARNING: File Calib_Results.mat already exists'); + pfn = -1; + cont = 1; + while cont, + pfn = pfn + 1; + postfix = ['_old'num2str(pfn)]; + save_name = [ 'Calib_Results' postfix]; + cont = (exist([ save_name '.mat'])==2); + end; + copyfile('Calib_Results.mat',[save_name '.mat']); + disp(['Copying the current Calib_Results.mat file to ' save_name '.mat']); +end; + + +save_name = 'Calib_Results'; + +if exist('calib_name'), + + fprintf(1,['\nSaving calibration results under ' save_name '.mat\n']); + + string_save = ['save ' save_name ' center_optim param_list active_images ind_active center_optim est_alpha est_dist fc kc cc alpha_c ex x y solution solution_init wintx winty n_ima type_numbering N_slots small_calib_image first_num image_numbers format_image calib_name Hcal Wcal nx ny map dX_default dY_default KK inv_KK dX dY']; + + for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' y_' num2str(kk) ' ex_' num2str(kk) ' omc_' num2str(kk) ' Rc_' num2str(kk) ' Tc_' num2str(kk) ' H_' num2str(kk) ' n_sq_x_' num2str(kk) ' n_sq_y_' num2str(kk) ' wintx_' num2str(kk) ' winty_' num2str(kk) ' dX_' num2str(kk) ' dY_' num2str(kk)]; + end; + +else + + fprintf(1,['\nSaving calibration results under ' save_name '.mat (no image version)\n']); + + string_save = ['save ' save_name ' center_optim param_list active_images ind_active center_optim est_alpha est_dist fc kc cc alpha_c ex x y solution solution_init wintx winty n_ima nx ny dX_default dY_default KK inv_KK dX dY']; + + for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' y_' num2str(kk) ' ex_' num2str(kk) ' omc_' num2str(kk) ' Rc_' num2str(kk) ' Tc_' num2str(kk) ' H_' num2str(kk) ' n_sq_x_' num2str(kk) ' n_sq_y_' num2str(kk) ' wintx_' num2str(kk) ' winty_' num2str(kk) ' dX_' num2str(kk) ' dY_' num2str(kk)]; + end; + +end; + + + +%fprintf(1,'To load later click on Load\n'); + +eval(string_save); + +fprintf(1,'done\n'); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/script_fit_distortion.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/script_fit_distortion.m new file mode 100755 index 0000000..c5e5430 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/script_fit_distortion.m @@ -0,0 +1,39 @@ + + satis_distort = 0; + + disp(['Estimated focal: ' num2str(f_g) ' pixels']); + + while ~satis_distort, + + k_g = input('Guess for distortion factor kc ([]=0): '); + + if isempty(k_g), k_g = 0; end; + + xy_corners_undist = comp_distortion2([x' - c_g(1);y'-c_g(2)]/f_g,k_g); + + xu = xy_corners_undist(1,:)'; + yu = xy_corners_undist(2,:)'; + + [XXu] = projectedGrid ( [xu(1);yu(1)], [xu(2);yu(2)],[xu(3);yu(3)], [xu(4);yu(4)],n_sq_x+1,n_sq_y+1); % The full grid + + XX = (ones(2,1)*(1 + k_g * sum(XXu.^2))) .* XXu; + XX(1,:) = f_g*XX(1,:)+c_g(1); + XX(2,:) = f_g*XX(2,:)+c_g(2); + + figure(2); + image(I); + colormap(map); + zoom on; + hold on; + %plot(f_g*XXu(1,:)+c_g(1),f_g*XXu(2,:)+c_g(2),'ro'); + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be on the grid corners...'); + hold off; + + satis_distort = input('Satisfied with distortion? ([]=no, other=yes) '); + + satis_distort = ~isempty(satis_distort); + + + end; + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/startup.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/startup.m new file mode 100755 index 0000000..aad0fa4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/startup.m @@ -0,0 +1,9 @@ +% Main camera calibration toolbox: + +calib_gui; + +%calib_gui; + +path(pwd,path); + +format compact diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/undistort_image.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/undistort_image.m new file mode 100755 index 0000000..d9a7574 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/undistort_image.m @@ -0,0 +1,193 @@ +%%% INPUT THE IMAGE FILE NAME: + +if ~exist('fc')|~exist('cc')|~exist('kc')|~exist('alpha_c'), + fprintf(1,'No intrinsic camera parameters available.\n'); + return; +end; + +KK = [fc(1) alpha_c*fc(1) cc(1);0 fc(2) cc(2) ; 0 0 1]; + +disp('Program that undistorts images'); +disp('The intrinsic camera parameters are assumed to be known (previously computed)'); + +fprintf(1,'\n'); + +quest = input('Do you want to undistort all the calibration images ([],0) or a new image (1)? '); + +if isempty(quest), + quest = 0; +end; + +if ~quest, + + if ~exist(['I_' num2str(ind_active(1))]), + ima_read_calib; + end; + + check_active_images; + + format_image2 = format_image; + if format_image2(1) == 'j', + format_image2 = 'bmp'; + end; + + for kk = 1:n_ima, + + if exist(['I_' num2str(kk)]), + + eval(['I = I_' num2str(kk) ';']); + [I2] = rect(I,eye(3),fc,cc,kc,KK); + + if ~type_numbering, + number_ext = num2str(image_numbers(kk)); + else + number_ext = sprintf(['%.' num2str(N_slots) 'd'],image_numbers(kk)); + end; + + ima_name2 = [calib_name '_rect' number_ext '.' format_image2]; + + fprintf(1,['Saving undistorted image under ' ima_name2 '...\n']); + + + if format_image2(1) == 'p', + if format_images2(2) == 'p', + saveppm(ima_name2,uint8(round(I2))); + else + savepgm(ima_name2,uint8(round(I2))); + end; + else + if format_image2(1) == 'r', + writeras(ima_name2,round(I2),gray(256)); + else + imwrite(uint8(round(I2)),gray(256),ima_name2,format_image2); + end; + end; + + + end; + + end; + + fprintf(1,'done\n'); + +else + + dir; + fprintf(1,'\n'); + + image_name = input('Image name (full name without extension): ','s'); + + format_image2 = '0'; + +while format_image2 == '0', + + format_image2 = input('Image format: ([]=''r''=''ras'', ''b''=''bmp'', ''t''=''tif'', ''p''=''pgm'', ''j''=''jpg'', ''m''=''ppm'') ','s'); + + if isempty(format_image2), + format_image2 = 'ras'; + end; + + if lower(format_image2(1)) == 'm', + format_image2 = 'ppm'; + else + if lower(format_image2(1)) == 'b', + format_image2 = 'bmp'; + else + if lower(format_image2(1)) == 't', + format_image2 = 'tif'; + else + if lower(format_image2(1)) == 'p', + format_image2 = 'pgm'; + else + if lower(format_image2(1)) == 'j', + format_image2 = 'jpg'; + else + if lower(format_image2(1)) == 'r', + format_image2 = 'ras'; + else + disp('Invalid image format'); + format_image2 = '0'; % Ask for format once again + end; + end; + end; + end; + end; + end; +end; + +ima_name = [image_name '.' format_image2]; + + +%%% READ IN IMAGE: + +if format_image2(1) == 'p', + if format_image2(2) == 'p', + I = double(loadppm(ima_name)); + else + I = double(loadpgm(ima_name)); + end; +else + if format_image2(1) == 'r', + I = readras(ima_name); + else + I = double(imread(ima_name)); + end; +end; + +if size(I,3)>1, + I = I(:,:,2); +end; + + +if (size(I,1)>ny)|(size(I,2)>nx), + I = I(1:ny,1:nx); +end; + + + %% SHOW THE ORIGINAL IMAGE: + + figure(2); + image(I); + colormap(gray(256)); + title('Original image (with distortion) - Stored in array I'); + drawnow; + + + %% UNDISTORT THE IMAGE: + + fprintf(1,'Computing the undistorted image...') + + [I2] = rect(I,eye(3),fc,cc,kc,alpha_c,KK); + + fprintf(1,'done\n'); + + figure(3); + image(I2); + colormap(gray(256)); + title('Undistorted image - Stored in array I2'); + drawnow; + + + %% SAVE THE IMAGE IN FILE: + + ima_name2 = [image_name '_rect.' format_image2]; + + fprintf(1,['Saving undistorted image under ' ima_name2 '...']); + + if format_image2(1) == 'p', + if format_images2(2) == 'p', + saveppm(ima_name2,uint8(round(I2))); + else + savepgm(ima_name2,uint8(round(I2))); + end; + else + if format_image2(1) == 'r', + writeras(ima_name2,round(I2),gray(256)); + else + imwrite(uint8(round(I2)),gray(256),ima_name2,format_image2); + end; + end; + + fprintf(1,'done\n'); + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_convert.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_convert.m new file mode 100755 index 0000000..8946349 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_convert.m @@ -0,0 +1,89 @@ +function [fc,cc,kc,Rc,Tc,omc,nx,ny] = willson_convert(Ncx,Nfx,dx,dy,dpx,dpy,Cx,Cy,sx,f,kappa1,Tx,Ty,Tz,Rx,Ry,Rz,p1,p2); + +%Conversion from Reg Willson's calibration format to my format + +% Conversion: + +% Focal length: +fc = [sx/dpx ; 1/dpy]*f; + +% Principal point; +cc = [Cx;Cy]; + +% Extrinsic parameters: +Rx = rodrigues([Rx;0;0]); +Ry = rodrigues([0;Ry;0]); +Rz = rodrigues([0;0;Rz]); + +Rc = Rz * Ry * Rx; + +omc = rodrigues(Rc); + +Tc = [Tx;Ty;Tz]; + + +% More tricky: Take care of the distorsion: + +Nfy = round(Nfx * 3/4); + +nx = Nfx; +ny = Nfy; + +% Select a set of DISTORTED coordinates uniformely distributed across the image: + +[xp_dist,yp_dist] = meshgrid(0:Nfx-1,0:Nfy); + +xp_dist = xp_dist(:)'; +yp_dist = yp_dist(:)'; + + +% Apply UNDISTORTION according to Willson: + +xp_sensor_dist = dpx*(xp_dist - Cx)/sx; +yp_sensor_dist = dpy*(yp_dist - Cy); + +dist_fact = 1 + kappa1*(xp_sensor_dist.^2 + yp_sensor_dist.^2); + +xp_sensor = xp_sensor_dist .* dist_fact; +yp_sensor = yp_sensor_dist .* dist_fact; + +xp = xp_sensor * sx / dpx + Cx; +yp = yp_sensor / dpy + Cy; + +ind= find((xp > 0) & (xp < Nfx-1) & (yp > 0) & (yp < Nfy-1)); + +xp = xp(ind); +yp = yp(ind); +xp_dist = xp_dist(ind); +yp_dist = yp_dist(ind); + + +% Now, find my own set of parameters: + +x_dist = [(xp_dist - cc(1))/fc(1);(yp_dist - cc(2))/fc(2)]; +x = [(xp - cc(1))/fc(1);(yp - cc(2))/fc(2)]; + +k = [0;0;0;0]; + +for kk = 1:5, + + [xd,dxddk] = apply_distortion(x,k); + + err = x_dist - xd; + + %norm(err) + + k_step = inv(dxddk'*dxddk)*(dxddk')*err(:); + + k = k + k_step; %inv(dxddk'*dxddk)*(dxddk')*err(:); + + %norm(k_step)/norm(k) + + if norm(k_step)/norm(k) < 10e-10, + break; + end; + +end; + + +kc = k; diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_read.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_read.m new file mode 100755 index 0000000..bbde63c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/willson_read.m @@ -0,0 +1,59 @@ +% Read in Reg Willson's data file, and convert it into my data format: + +%dir; + +%calib_file = input('Reg Willson calibration file name: ','s'); + +if exist(calib_file), + + + load(calib_file); + + inddot = find(calib_file == '.'); + + if isempty(inddot), + varname = calib_file; + else + varname = calib_file(1:inddot(1)-1); + end; + + eval(['calib_params = ' varname ';']) + + Ncx = calib_params(1); + Nfx = calib_params(2); + dx = calib_params(3); + dy = calib_params(4); + dpx = calib_params(5); + dpy = calib_params(6); + Cx = calib_params(7); + Cy = calib_params(8); + sx = calib_params(9); + f = calib_params(10); + kappa1 = calib_params(11); + Tx = calib_params(12); + Ty = calib_params(13); + Tz = calib_params(14); + Rx = calib_params(15); + Ry = calib_params(16); + Rz = calib_params(17); + p1 = calib_params(18); + p2 = calib_params(19); + + % Conversion: + [fc,cc,kc,Rc_1,Tc_1,omc_1,nx,ny] = willson_convert(Ncx,Nfx,dx,dy,dpx,dpy,Cx,Cy,sx,f,kappa1,Tx,Ty,Tz,Rx,Ry,Rz,p1,p2); + + KK = [fc(1) 0 cc(1);0 fc(2) cc(2) ; 0 0 1]; + + n_ima = 1; + + X_1 = [NaN;NaN;NaN]; + x_1 = [NaN;NaN]; + + map = gray(256); + +else + + disp(['WARNING: Calibration file ' calib_file ' not found']); + +end; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/writeras.m b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/writeras.m new file mode 100755 index 0000000..c7eb7bc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/TOOLBOX_calib/writeras.m @@ -0,0 +1,105 @@ +function writeras(filename, image, map); +%WRITERAS Write an image file in sun raster format. +% WRITERAS('imagefile.ras', image_matrix, map) writes a +% "sun.raster" image file. + +% Written by Thomas K. Leung 3/30/93. +% @ California Institute of Technology. + + +% PC and UNIX version of writeras - Jean-Yves Bouguet - Dec. 1998 + +dot = max(find(filename == '.')); +suffix = filename(dot+1:dot+3); + +if nargin < 3, + map = []; +end; + +if(strcmp(suffix, 'ras')) + %Write header + + fp = fopen(filename, 'wb'); + if(fp < 0) error(['Cannot open ' filename '.']), end + + [height, width] = size(image); + image = image - 1; + mapsize = size(map, 1)*size(map,2); + %fwrite(fp, ... + % [1504078485, width, height, 8, height*width, 1, 1, mapsize], ... + % 'long'); + + + zero_str = '00000000'; + + % MAGIC NUMBER: + + + fwrite(fp,89,'uchar'); + fwrite(fp,166,'uchar'); + fwrite(fp,106,'uchar'); + fwrite(fp,149,'uchar'); + + width_str = dec2hex(width); + width_str = [zero_str(1:8-length(width_str)) width_str]; + + for ii = 1:2:7, + fwrite(fp,hex2dec(width_str(ii:ii+1)),'uchar'); + end; + + + height_str = dec2hex(height); + height_str = [zero_str(1:8-length(height_str)) height_str]; + + for ii = 1:2:7, + fwrite(fp,hex2dec(height_str(ii:ii+1)),'uchar'); + end; + + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,8,'uchar'); + + ll = height*width; + ll_str = dec2hex(ll); + ll_str = [zero_str(1:8-length(ll_str)) ll_str]; + + for ii = 1:2:7, + fwrite(fp,hex2dec(ll_str(ii:ii+1)),'uchar'); + end; + + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,1,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,~~mapsize,'uchar'); + + mapsize_str = dec2hex(mapsize); + mapsize_str = [zero_str(1:8-length(mapsize_str)) mapsize_str]; + + %keyboard; + + for ii = 1:2:7, + fwrite(fp,hex2dec(mapsize_str(ii:ii+1)),'uchar'); + end; + + + if mapsize ~= 0 + map = min(255, fix(255*map)); + fwrite(fp, map, 'uchar'); + end + if rem(width,2) == 1 + image = [image'; zeros(1, height)]'; + top = 255 * ones(size(image)); + fwrite(fp, min(top,image)', 'uchar'); + else + top = 255 * ones(size(image)); + fwrite(fp, min(top,image)', 'uchar'); + end + fclose(fp); +else + error('Image file name must end in either ''ras'' or ''rast''.'); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/README b/SD-VBS/common/toolbox/toolbox_basic/affine/README new file mode 100755 index 0000000..e578a74 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/README @@ -0,0 +1,5 @@ +Top level program is "compute_AD.m". Use "compute_AD_disp.m" if one +wants to display results as program runs. + +The testing programs are called "simulation.m" for synthetic images, +and "test_affine.m" for real images. diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/carve_it.m b/SD-VBS/common/toolbox/toolbox_basic/affine/carve_it.m new file mode 100755 index 0000000..1a44f89 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/carve_it.m @@ -0,0 +1,25 @@ +function img = carve_it(I,center,window_size_h) + +[size_y,size_x]= size(I); +min_x = round(center(1)-window_size_h(1)); +max_x = round(center(1)+window_size_h(1)); +min_y = round(center(2)-window_size_h(2)); +max_y = round(center(2)+window_size_h(2)); +window_size = window_size_h*2 +1; + +if (min_x <1)|(max_x > size_x)|(min_y<1)|(max_y>size_y), + disp('window too big'); + center + window_size_h + img = zeros(window_size(2),window_size(1)); + n_min_x = max(1,round(min_x)); + n_min_y = max(1,round(min_y)); + n_max_x = min(size_x,round(max_x)); + n_max_y = min(size_y,round(max_y)); + img(1+(n_min_y-min_y):window_size(2)-(max_y-n_max_y),1+(n_min_x-min_x):window_size(1)-(max_x-n_max_x))=I(n_min_y:n_max_y,n_min_x:n_max_x); +else + img = I(center(2)-window_size_h(2):center(2)+window_size_h(2),... + center(1)-window_size_h(1):center(1)+window_size_h(1)); +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD.m b/SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD.m new file mode 100755 index 0000000..a39acd6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD.m @@ -0,0 +1,90 @@ +function [A,D,mask] =... +compute_AD(img_i,img_j,center_i,center_j,window_size_h,num_iter,w,num_trans,Dest,mask) +% +% function [A,D,mask] = ... +% compute_AD(img_i,img_j,center_i,center_j,window_size_h,num_iter,w, +% mask,num_trans) +% +% A: Affine motion; +% D: Displacement; +% +% img_i, img_j: the two image(in full size); +% center_i, center_j: the centers of the feature in two images; +% window_size_h: half size of the feature window; +% num_iter: number of iterations; +% w: parameter used in "grad.m" for computing gaussians used for +% gradient estimation; +% +% num_trans: OPTIONAL, number of translation iteration; default = 3; +% mask: OPTIONAL, if some area of the square shaped feature window should +% be weighted less; +% + +% +% Jianbo Shi +% + +if ~exist('Dest'), + Dest = [0,0]; +end + +if ~exist('mask'), + mask = ones(2*window_size_h+1)'; +end + +% set the default num_trans +if ~exist('num_trans'), + num_trans= 3; +end + +% normalize image intensity to the range of 0.0-1.0 +img_i = norm_inten(img_i); +img_j = norm_inten(img_j); + +window_size = 2*window_size_h + 1; +I = carve_it(img_i,center_i,window_size_h); +J = carve_it(img_j,center_j,window_size_h); + +% init. step +J_computed = I; +D_computed = Dest; +A_computed = eye(2); +J_computed = compute_J(A_computed,D_computed,img_i,center_i,window_size_h); + +%% level of noise +sig = 0.1; + +records = zeros(num_iter,6); +errs = zeros(1,num_iter); + +k = 1; +% iteration +while k <= num_iter, + [A,D] = iter_AD(J_computed,J,mask,w,k,num_trans); + + A_computed = A*A_computed; + D_computed = (A*D_computed')' + D; + + % compute the warped image + J_computed = compute_J(A_computed,D_computed,img_i,center_i,window_size_h); + + % compute the SSD error + errs(k) = sqrt(sum(sum((mask.*(J_computed-J)).^2)))/prod(size(J)); + + % update the mask, discounting possible occlusion region + if (k>num_trans), + mask = exp(-abs(J_computed-J)/sig); + end + + % record the A and D + records(k,:) = [reshape(A_computed,1,4),reshape(D_computed,1,2)]; + + k = k+1; +end + +[tmp,id] = min(errs); +A = reshape(records(id,1:4),2,2); +D = reshape(records(id,5:6),1,2); + +J_computed = compute_J(A,D,img_i,center_i,window_size_h); +mask = exp(-abs(J_computed-J)/sig); diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD_disp.m b/SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD_disp.m new file mode 100755 index 0000000..f2e6c62 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/compute_AD_disp.m @@ -0,0 +1,103 @@ +function [A,D,mask] =... +compute_AD_disp(img_i,img_j,center_i,center_j,window_size_h,num_iter,w,fig_disp,num_trans,Dest,mask) +% +% function [A,D,mask] = ... +% compute_AD_disp(img_i,img_j,center_i,center_j,window_size_h,num_iter,w, +% fig_disp,mask,num_trans) +% +% Computing affine transform for matching to image patches. Display results +% as program runs. +% +% A: Affine motion; +% D: Displacement; +% +% +% img_i, img_j: the two image(in full size); +% center_i, center_j: the centers of the feature in two images; +% window_size_h: half size of the feature window; +% num_iter: number of iterations; +% w: parameter used in "grad.m" for computing gaussians used for +% gradient estimation; +% fig_disp: figure for display; +% +% num_trans: OPTIONAL, number of translation iteration; +% mask: OPTIONAL, if some area of the square shaped feature window should +% be weighted less; +% + + +% +% Jianbo Shi +% + +if ~exist('mask'), + mask = ones(2*window_size_h+1)'; +end + +if ~exist('Dest'), + Dest = [0,0]; +end + +% set the default num_trans +if ~exist('num_trans'), + num_trans= 3; +end + +% normalize image intensity to the range of 0.0-1.0 +img_i = norm_inten(img_i); +img_j = norm_inten(img_j); + +window_size = 2*window_size_h + 1; +I = carve_it(img_i,center_i,window_size_h); +J = carve_it(img_j,center_j,window_size_h); + +% init. step +D_computed = Dest; +A_computed = eye(2); +J_computed = compute_J(A_computed,D_computed,img_i,center_i,window_size_h); + + + +figure(fig_disp);subplot(1,3,1);imagesc(I);colormap(gray);axis('image'); +subplot(1,3,3);imagesc(J);axis('image'); +drawnow; + +sig = 0.1; + +records = zeros(num_iter,6); +errs = zeros(1,num_iter); + +k = 1; +% iteration +while k <= num_iter, + [A,D] = iter_AD(J_computed,J,mask,w,k,num_trans); + + A_computed = A*A_computed; + D_computed = (A*D_computed')' + D; + + % compute the warped image + J_computed = compute_J(A_computed,D_computed,img_i,center_i,window_size_h); + + % compute the SSD error + errs(k) = sqrt(sum(sum((mask.*(J_computed-J)).^2)))/prod(size(J)) + + % update the mask, discounting possible occlusion region + if (k>num_trans+1), + mask = exp(-abs(J_computed-J)/sig); + end + + % record the A and D + records(k,:) = [reshape(A_computed,1,4),reshape(D_computed,1,2)]; + + figure(fig_disp);subplot(1,3,2);imagesc(J_computed);axis('image'); + title(sprintf('iter:%d: dx=%3.3f, dy = %3.3f',k,D_computed(1),D_computed(2)));drawnow; + + k = k+1; +end + +[tmp,id] = min(errs); +A = reshape(records(id,1:4),2,2); +D = reshape(records(id,5:6),1,2); + +J_computed = compute_J(A,D,img_i,center_i,window_size_h); +mask = exp(-abs(J_computed-J)/sig); diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/compute_J.m b/SD-VBS/common/toolbox/toolbox_basic/affine/compute_J.m new file mode 100755 index 0000000..80db273 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/compute_J.m @@ -0,0 +1,31 @@ +function [JJ,mask] = compute_J(A,D,I,center,window_size_h) +%% function J = compute_J(A,D,I,center,window_size_h) +% + +[size_y,size_x] = size(I); + +center_x = center(1); +center_y = center(2); + +[XX,YY] = meshgrid(1:size_x,1:size_y); +x = reshape(XX,size_x*size_y,1); +y = reshape(YY,size_x*size_y,1); +index(:,1) = x-center_x; +index(:,2) = y-center_y; + +position_new = A*index'+ [D(1),0;0,D(2)]*ones(2,size_x*size_y); +position_new(1,:) = position_new(1,:)+center_x; +position_new(2,:) = position_new(2,:)+center_y; + +position_new_x = reshape(position_new(1,:),size_y,size_x); +position_new_y = reshape(position_new(2,:),size_y,size_x); + +[J,mask]= m_interp4(I,position_new_x,position_new_y); + +JJ = J(center(2)-window_size_h(2):center(2)+window_size_h(2),... + center(1)-window_size_h(1):center(1)+window_size_h(1)); +mask = mask(center(2)-window_size_h(2):center(2)+window_size_h(2),... + center(1)-window_size_h(1):center(1)+window_size_h(1)); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/find_AD.m b/SD-VBS/common/toolbox/toolbox_basic/affine/find_AD.m new file mode 100755 index 0000000..3cccefb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/find_AD.m @@ -0,0 +1,82 @@ +function [A,D] = find_AD(I,J,mask,w) +% +% [A,D] = find_AD(I,J,mask,w) +% +% find the matrix affine transform A and displacement D, +% such that SSD difference of I(Ax-d)-J(x) is minimized, +% + +% +% Jianbo Shi +% + + +[gy1,gx1] = grad(I,w); +[gy2,gx2] = grad(J,w); + +gx = 0.5*(gx1+gx2); +gy = 0.5*(gy1+gy2); + +[size_y,size_x] = size(I); +[center_y,center_x] = find_center(size_y,size_x); +mask = mask(w+1:size_y-w,w+1:size_x-w); + +[x,y] = meshgrid(1:size_x,1:size_y); +x = x- center_x; +y = y-center_y; + +x = x(w+1:size_y-w,w+1:size_x-w); +y = y(w+1:size_y-w,w+1:size_x-w); + +gx_sqr = gx.*mask.*gx; +gx_gy = gx.*mask.*gy; +gy_sqr = gy.*mask.*gy; + +x_sqr = x.*x; +x_y = x.*y; +y_sqr = y.*y; + +T= zeros(6,6); +T(1,1) = 0.5*trapz(trapz(gx_sqr.*x_sqr)); +T(2,1) = trapz(trapz(gx_gy.*x_y)); +T(3,1) = trapz(trapz(gx_sqr.*x_y)); +T(4,1) = trapz(trapz(gx_gy.*x_sqr)); +T(5,1) = trapz(trapz(gx_sqr.*x)); +T(6,1) = trapz(trapz(gx_gy.*x)); +T(2,2) = 0.5*trapz(trapz(gy_sqr.*y_sqr)); +T(3,2) = trapz(trapz(gx_gy.*y_sqr)); +T(4,2) = trapz(trapz(gy_sqr.*x_y)); +T(5,2) = trapz(trapz(gx_gy.*y)); +T(6,2) = trapz(trapz(gy_sqr.*y)); +T(3,3) = 0.5*trapz(trapz(gx_sqr.*y_sqr)); +T(4,3) = trapz(trapz(gx_gy.*x_y)); +T(5,3) = trapz(trapz(gx_sqr.*y)); +T(6,3) = trapz(trapz(gx_gy.*y)); +T(4,4) = 0.5*trapz(trapz(gy_sqr.*x_sqr)); +T(5,4) = trapz(trapz(gx_gy.*x)); +T(6,4) = trapz(trapz(gy_sqr.*x)); +T(5,5) = 0.5*trapz(trapz(gx_sqr)); +T(6,5) = trapz(trapz(gx_gy)); +T(6,6) = 0.5*trapz(trapz(gy_sqr)); + +T = T+T'; + +J = J(w+1:size_y-w,w+1:size_x-w); +I = I(w+1:size_y-w,w+1:size_x-w); + + +diff = (J-I).*mask; +b(1) = trapz(trapz(diff.*gx.*x)); +b(2) = trapz(trapz(diff.*gy.*y)); +b(3) = trapz(trapz(diff.*gx.*y)); +b(4) = trapz(trapz(diff.*gy.*x)); +b(5) = trapz(trapz(diff.*gx)); +b(6) = trapz(trapz(diff.*gy)); + +a = inv(T)*b'; + +A = [1+a(1), a(3); +a(4),1+a(2)]; + +D= [a(5),a(6)]; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/find_D.m b/SD-VBS/common/toolbox/toolbox_basic/affine/find_D.m new file mode 100755 index 0000000..1e42cb2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/find_D.m @@ -0,0 +1,65 @@ +function D = find_D(I,J,mask,w) +% +% function D = find_D(I,J,mask,w) +% +% find the vector D such that it minimizes then +% difference between I(Ax-d)-J(x). +% +% mask: the weight matrix, +% w: window size for estimating gradiant, use a large value +% when A,D are large. +% + +% +% NOTE: Because gradient values on the boarder regions of +% I and J can not be computed accuately when using +% a gaussian of large support, those boarder regions +% of width w are not used in computing D. +% + +% +% Jianbo Shi +% + +[gy1,gx1] = grad(I,w); +[gy2,gx2] = grad(J,w); + +gx = 0.5*(gx1+gx2); +gy = 0.5*(gy1+gy2); + +[size_y,size_x] = size(I); +[center_y,center_x] = find_center(size_y,size_x); +mask = mask(w+1:size_y-w,w+1:size_x-w); + +[x,y] = meshgrid(1:size_x,1:size_y); +x = x- center_x; +y = y-center_y; + +x = x(w+1:size_y-w,w+1:size_x-w); +y = y(w+1:size_y-w,w+1:size_x-w); + +gx_sqr = gx.*mask.*gx; +gx_gy = gx.*mask.*gy; +gy_sqr = gy.*mask.*gy; + + +T= zeros(2,2); + +T(1,1) = 0.5*trapz(trapz(gx_sqr)); +T(2,1) = trapz(trapz(gx_gy)); +T(2,2) = 0.5*trapz(trapz(gy_sqr)); + +T = T+T'; + +J = J(w+1:size_y-w,w+1:size_x-w); +I = I(w+1:size_y-w,w+1:size_x-w); + + +diff = (J-I).*mask; +b(1) = trapz(trapz(diff.*gx)); +b(2) = trapz(trapz(diff.*gy)); + +a = inv(T)*b'; + +D= [a(1),a(2)]; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/find_center.m b/SD-VBS/common/toolbox/toolbox_basic/affine/find_center.m new file mode 100755 index 0000000..b12ac7b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/find_center.m @@ -0,0 +1,4 @@ +function [center_x,center_y] = find_center(size_x,size_y) + +center_x = 0.5*(size_x +1); +center_y = 0.5*(size_y +1); diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/gen_feature_s.m b/SD-VBS/common/toolbox/toolbox_basic/affine/gen_feature_s.m new file mode 100755 index 0000000..3c113e9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/gen_feature_s.m @@ -0,0 +1,17 @@ +function I = gen_feature_s(size_of_feature) +% function I = gen_feature(size_of_feature) +% generates a spherical features with size +% of "size_of_feature" +% + +ss = round(0.4*size_of_feature); +[X,Y,II] = hemisphere_s(ss); + +II = abs(II); +II = 1/max(max(II))*II; + +I = zeros(size_of_feature,size_of_feature); + +t = round((size_of_feature-ss)/2); + +I(1+t:1+t+ss,1+t:1+t+ss) = II; diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/grad.m b/SD-VBS/common/toolbox/toolbox_basic/affine/grad.m new file mode 100755 index 0000000..53bab55 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/grad.m @@ -0,0 +1,24 @@ +% gradient of an image +% coordinates (r, c) follow matrix convention; +% the gaussian is truncated at x = +- tail, and there are samples samples +% inbetween, where samples = hsamples * 2 + 1 + +function[gr,gc] = gradient(image, hsamples) + +tail=4; +samples = hsamples * 2 + 1; + +x = linspace(-tail, tail, samples); +gauss = exp(-x.^2); +n = gauss * ones(samples,1); +gauss = gauss/n; + +gaussderiv = -x.*gauss; +n = -gaussderiv*linspace(1,samples,samples)'; +gaussderiv = gaussderiv/n; + +gr = conv2(conv2(image, gaussderiv','valid'), gauss,'valid'); +gc = conv2(conv2(image, gaussderiv,'valid'), gauss','valid'); + +%gr = conv2(conv2(image, gaussderiv','same'), gauss,'same'); +%gc = conv2(conv2(image, gaussderiv,'same'), gauss','same'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/hemisphere_s.m b/SD-VBS/common/toolbox/toolbox_basic/affine/hemisphere_s.m new file mode 100755 index 0000000..5300183 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/hemisphere_s.m @@ -0,0 +1,27 @@ +function [x,y,z] = hemisphere(r) +%HEMISPHERE Generate sphere and transform from spherical coordinates. +% +% [X,Y,Z] = HEMISPHERE(N) generates three (n+1)-by-(n+1) +% matrices so that SURF(X,Y,Z) produces a sphere. +% +% [X,Y,Z] = HEMISPHERE(R,N) generates three (n+1)-by-(n+1) +% matrices so that SURF(X,Y,Z,R) produces a sphere colored by R +% +% [X,Y,Z] = HEMISPHERE(R,THETA,PHI) converts from spherical coordinates +% to cartesian coordinates. + +% Modified from +% Clay M. Thompson 4-24-91 +% Copyright (c) 1991-92 by the MathWorks, Inc. +% by Carlo Tomasi + +error(nargchk(1,3,nargin)); + +n = r; +% 0 <= theta <= 2*pi and 0 <= phi <= pi/2 +[theta,phi] = meshgrid((pi/n/2)*[-n:2:n],(pi/2/n)*[-n:2:n]); +r = ones(n+1,n+1); + +x = r .* cos(phi) .* sin(theta); +y = r .* sin(phi); +z = r .* cos(phi) .* cos(theta).*phi.*theta; diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/im.m b/SD-VBS/common/toolbox/toolbox_basic/affine/im.m new file mode 100755 index 0000000..6450120 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/im.m @@ -0,0 +1,3 @@ +function im(I) + +imagesc(I);axis('image');drawnow; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/iter_AD.m b/SD-VBS/common/toolbox/toolbox_basic/affine/iter_AD.m new file mode 100755 index 0000000..50bdae1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/iter_AD.m @@ -0,0 +1,26 @@ +function [A,D] = iter_AD(I,J,mask,w,k,num_trans) +% +% function [A,D] = iter_AD(I,J,mask,w,k,num_trans) +% +% find the affine motion A, and displacement D, +% such that difference between I(Ax-D) and J(x) is minimized. +% If k <= num_trans, only translation is computed. This is useful +% in practice, when translation is relative large. +% +% mask: the weight matrix, +% w: window size for estimating gradiant, use a large value +% when A,D are large. +% + +% +% Jianbo Shi +% + + +if k <= num_trans, + D = find_D(I,J,mask,w); + A = eye(2); +else + [A,D] = find_AD(I,J,mask,w); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/m_interp4.m b/SD-VBS/common/toolbox/toolbox_basic/affine/m_interp4.m new file mode 100755 index 0000000..314f140 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/m_interp4.m @@ -0,0 +1,49 @@ +function [F,mask] = m_interp4(z,s,t) +%INTERP4 2-D bilinear data interpolation. +% ZI = INTERP4(Z,XI,YI) assumes X = 1:N and Y = 1:M, where +% [M,N] = SIZE(Z). +% +% Copyright (c) 1984-93 by The MathWorks, Inc. +% Clay M. Thompson 4-26-91, revised 7-3-91, 3-22-93 by CMT. +% +% modified to + + +[nrows,ncols] = size(z); + + +if any(size(z)<[3 3]), error('Z must be at least 3-by-3.'); end +if size(s)~=size(t), error('XI and YI must be the same size.'); end + +% Check for out of range values of s and set to 1 +sout = find((s<1)|(s>ncols)); +if length(sout)>0, s(sout) = ones(size(sout)); end + +% Check for out of range values of t and set to 1 +tout = find((t<1)|(t>nrows)); +if length(tout)>0, t(tout) = ones(size(tout)); end + +% Matrix element indexing +ndx = floor(t)+floor(s-1)*nrows; + +% Compute intepolation parameters, check for boundary value. +d = find(s==ncols); +s(:) = (s - floor(s)); +if length(d)>0, s(d) = s(d)+1; ndx(d) = ndx(d)-nrows; end + +% Compute intepolation parameters, check for boundary value. +d = find(t==nrows); +t(:) = (t - floor(t)); +if length(d)>0, t(d) = t(d)+1; ndx(d) = ndx(d)-1; end +d = []; + +% Now interpolate, reuse u and v to save memory. +F = ( z(ndx).*(1-t) + z(ndx+1).*t ).*(1-s) + ... + ( z(ndx+nrows).*(1-t) + z(ndx+(nrows+1)).*t ).*s; + +mask = ones(size(z)); + +% Now set out of range values to zeros. +if length(sout)>0, F(sout) = zeros(size(sout));mask(sout)=zeros(size(sout));end +if length(tout)>0, F(tout) = zeros(size(tout));mask(tout)=zeros(size(tout));end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/norm_inten.m b/SD-VBS/common/toolbox/toolbox_basic/affine/norm_inten.m new file mode 100755 index 0000000..8e8865b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/norm_inten.m @@ -0,0 +1,11 @@ +function I = norm_inten(J) +% +% I = norm_inten(J) +% +% normalize image intensity to the range of 0.0-1.0 +% + +max_J = max(max(J)); +min_J = min(min(J)); + +I = (J-min_J)/(max_J-min_J); diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/pan.0.pgm b/SD-VBS/common/toolbox/toolbox_basic/affine/pan.0.pgm new file mode 100755 index 0000000..2e7b5f6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/pan.0.pgm @@ -0,0 +1,53 @@ +P5 +# CREATOR: XV Version 3.10a Rev: 12/29/94 +128 96 +255 +qquvvvvwzyz{}~qrrsvxwxyzzz{{|~~tuwuruzzyzz{}}}tvwxyxwwz{{z|{|}tvwwyz{ywz}}mzuuvvvurvuwxz{{{}BUwxz}~}~{{GcZW}}~}~Geoxae~~ηFakswveyӺùIbjnpu|x_nbb_eq¿漭㖽>q}̶Gbjkloqw|pnrcbbehq.ٻfbf0^xF_VYqH^ehkoopqyf|x;33>AVyi5!qcMi<2<3WWB=XFoZMqeHNtMdfVwNiv\uat}c*dHY]ZtG]aced`C233++%)(*59:@7B>F>FEA=<\g^gb\^Ya][\PD83.2X_=Hj?42)Wh3%GkgpD8xb`vJQjFktVR|LvtTytLU{OdoU}N]sSwZl[+eIVYRqI[^___W?519+&'112>;9G<@;E8BG<:5V[FYYLPIHHMA4=A83:PMd{m@BdmUOi?$ByC!'Q_pS$l|hSrJMqG^ugpOs~Rq|QRN\wM}SYyQuPeX#_NQ_RpFX\]^YK512:)&(032>?9B=E:G7AJ@B;W\GRWHOHEJIB5>C@:4NQ^xp<5[njFlJ, %pIQk]L*&Xw{qReNBnIGeԨYjKayPRXQQuXQOtUb^%[QPcRoGUY]YNC445=)%+.01?98EAG=E6>H@B7BFRwvN\r>itK2WRJmRZ9(AepvPZ[BpVCfΨU]N[zSKgUPjbJQpX\d%UVPhNmFUYYTG>26;?*'+247><;HCM@I7<J@H:;JAF>O[SM_PR]XVLSJ8>8565BD_v>)hiEwU-"lLGpcaHA$U~o~wRjLJrCCZuDtsF}yPqSmlTMsY`tZ~2CeMrRqIRSMGMXPF>A.**0.APIR>L?9K;DEQ]XP`VS]\[OML5?C8?7BDX|~R[sBm_7[_"=cqfj_W;Is|~T\[HvRCa^DxGjHqPgW\yXSee^Z9@kLrPmGOOIM[\MC>@+(*-,7>;GAJYUU_ZU^ZOKGP?IH2?5>GRue&KuK]gH#DoNXhj]FE]jb|rvZRmIp_?fbCxV[NdQ\[UQyW`o[\A<qNvPmHOKFM[\NB>?)(,,+>BBDSHP@FA8ICG@F\WV]bS_\KLISOED2:4:FNpt85teThQ+*ENWOG6*+;PVm|xkPpK`pVpzuoZYW_ZhPPm`[{[\}L;~wSvQiFIHDLXVJ@<>(''+)7AEESGO4*))(ATq~Q[VIt<6:RTq}UVV|a^x]^afV+nOoS]EFEELOB/&5?ABREK=CI;KLIH=b_\YVPMT[cZVVGDA4<6>BT}m+N|\[f/"$');GGC7,%2W]l^UhHk:+DEZ`WwTrVmn]Xbyeb\%hQlV]GGHDJL;+.%%1'1-!?L@GMIN@\aYTVVQWgaWPMKIC1:69* ,NUVzmV{GW5 #FBuhZUj\cXiXW}epmcc(^Pf[[EEFDJH4$&(COEZsW.Q=GQDR:V]U^]`_`\^[M]VOI8868CIf_!a_.')/3:1-35.+)#,DPFcbdY:1,;M[Xv\m\k[\~Wopgas(TUafUBCGBF:&&""$.ReV4 8A?NHDJ>O>DPISAP][dcb[ZSZfX`QBH?37/AD\u0F~T.31./:?:'9DBGJW}dVj@5"3@XXWo^}_xY[nbjyjd|+R[ZlWDFGDA3+,!12'6FDIKJO>RBGUMUCQdcjfaWWT^gZXYFHK291KhZuWHGDDE.#  +!"9HGJOLUCPEHRKMFJaf`XW^_e`^aUa^KI5:.,FLkd$I#:WRQXXiy|wzs;1/]af]%! DuZg\g`Wh_i}ecje|EDiXzVEGFDC1-)%0ZdH')3;=HGEMKVBQJJTJNHHfc`WOjg_X]dXabOK674+EIdp6 +!Njv}}v>-"S~vz{csbb`fdgaohxXAs\[GFFFC/44,-ZkL+*0=AGFCILUHQLJPAMHA^ZX[OkcVXdfYX]LLA3:1>K^C $Tgmt{vuz{}|>%R|pnhs^qbmZzkfr]AOUaga^bcehnppprvvvtw{xz{x~v)+UL>516HZU?8?QhsE!cFFIL[bcfjlnoqruwyyz|~~}}zE:\?+)69:|=.0BTx^28^nmkijiiijfffdbcb`aa`_]^^``_\\\\]^`__a`b`bdb_GHJM[XPNOQTTVVWYZZ[[\\^]]^_`aabbbbbbcdfgfggghikllmoppqqrqqrrsrB>eSLIE:[C2,94>BB.7AB11< '' +"5 $EXg~p|W5((..!!%GHN)   5*U{`_uH\h_xfDt|~Ngk!  /t_$$&T={@^z @U\iuxxzxs3Zs-AyjvO@2RRIQMxxst~KNNNOO_WesG_jbxfHw}Nnj   +5|]$##Z??bpQdPY`inr}|u}e7Vu%Ovlˁz8HwgMeZxvrrLNNNVgjYgvKegcygJx}~Qsi   !#3_O#>EL\cOONWWRT]kqkozd:1><"2LTSqgM=FNHFIUqrqpOPRRdmkXfuQjhg~jLzPxf + + +#&!]mOKIOY_badin\G:6$WZbܬNRJDKB>BgqvvoMNQVgolUiv[sdcqN}S|b  +  +%%ۼ׎GOPMKQ\\SS=&FX[f\KWZOUOF|uzz~tMPRXjqlJb|e{ebyzV~|[\ +   + !$' صnILJG;qVfqkCYaDGZbOVWQzwzKMPXinkC[}awhb|Y|\Z +    $%>뷗\{S@Fb羥lY\Z<=5czK^nb{}t{KMQXjojFWfyqf`|_Y  +  '& K؞h]j7PwO|w~pzLNOZloh>Ncsyh[|}aY    %&N﷜exhG?5Lp^;^zs~qwLMOZloh>Fcpmc~b_   '%8鼸9>~p^k}bz\w! +  )' xf\WVIHnjz|ϴ{kMOOZoqhE9~`pwbva|*  + ()a`Ê|ɰýqbNNP^ppiC0{ayujql4 % +"*(gaG]uVŭùqSMNQ^qohI&jYfuqdsz6 + ! !)+ >S_s²{p~JMMO_wwpcVapXWkWr{OKfpk' $!#..!^ef}rWh¥x¸ſx{DKOQbrrtwy|GNBF7sgkwŭ¼s?MPRYF997:<><>>><<;;:<AEINS[aflqtx}vͽILLMB$ LԲIKKJD-  T׶#&$# ! !  !!JKLIF7  PӻC&$#"  !""!"""!"#"%$EJLHC;' ]ñw%&%##"!#"!"#"#""""#$$&'IJIHF?. u›ľ2)&$"#$#!"""#"#&%%&)(()HJKGDA7$  &Ư߲W,(%%%$"#$####$&&()(+.0GHJJHB=, @衤$('''%%&''&%&&'(),*,/2FGFGEB?2 [ԯ❝9-))('%'''&&((+---.022GHHGFB?7& oϦۻʙn+++(()'&%&'*+--.04333FFFGFB?9*~קַ§.-+*((')()+,-./013444FGJIGDA;0  "ݵ𳷽S5/++,+++-/./10362251DGGGEB@<4& BҰʴ⡶)-.,,+*,.00122244444FFEEDA@=8- _ླྀ±źћ60,,-.../0334555553/BBDDCB?;6." y̹ܵǹđn-.-/../112333335421EDDEB@?<80% 6ѕIJ6.,/121134464543244ADECAA?<93' NⲫĹ̬Z20012/233121132122@?B@A?A=:4+!dͷȵžæ/44314442034532441BABBCB@><82& љĬ}Q84443444222333121>@CDDCB?<93(Dú݊bnt88432421255422122=>@AABA@<94+"nݱ캙vkwwyyWpvpU973222122233220252@AABCB@><97.#(rH 9ltvpmb\ZZrT !&nks2.BS@78423322312311221/11>@ABABA=<;72'(0&;_ggea\SQU]iSflV7@CDK<3443223322334443233110=>CCBBAB@<94)!  ""!!"""##%"#&'!(AW^_``\XY^XWbP%xmZ8:>FNI:899:7878557677766777554>?@BABB@?<95. !!"""##$%%##$%%&((''(-5@LSWYXVTRKDI^`hz`5*, /uaX=;=DOYI889:9::999879::999:98886 \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/pan.1.pgm b/SD-VBS/common/toolbox/toolbox_basic/affine/pan.1.pgm new file mode 100755 index 0000000..9d57f70 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/pan.1.pgm @@ -0,0 +1,59 @@ +P5 +# CREATOR: XV Version 3.10a Rev: 12/29/94 +128 96 +255 +prsrxxwxz{{||||}~~ſ¿prsttuyxyyz{|{{|~tuuuwwvwyxzz{|}tuwwxxzyy{{{{||}~uvuwyyz{~{z~~~_`k{x{{zzywzyz{{{}~~8:E[{|}57@_WYĿ:9D`ny_iδ8:B^kryxc}ѷ¸8;C[gorv}wbned_gxߍ0mt9;D^iloquz~nqxifdefq1ͺ؁x^[rk4\|JSZ87A[gjknqtw{f䍀y962??Xvb0 nWGa7-84Z`HA`Hu_NulKQwIaiTxNcw]uZm|i&[~KQ_8:BZdeffcK5850)%)09::B6BAIAGIEB>]linhbc[e_^aRC71/4=Eera?=Y_=He<30(Wd0$FkenD9y_]wKQlImuVS|LvvSvrKT{MbqS|R[vSvUb]$\KM`;;CX`cba[A2/41)&2-/=74CC8H8?H?EUYVQF>7:>A,%(2-6A:;N@KBL:7L?G@UYJ@XEMQP\GTF@@4:58EHh}m/ 6nXOmG*3q<S{jJ$)&lwqnUvG[n>FUhJcLqV|StbWMzTcoY~3CiLq??ESURJA=46A+)*+*7>>>LEL>I?7G+,,0+<>>ES@KDGC;GBHBF^^X]aP[WEKBRNGC0969GKoq45teWjJERTOC3,7O_K*#h}{oRqJanUirwqW]U`WkRQne[Z]sT1t~PrACEJIFKYWLB>>'#(-*8>>@REM) + &':=@CSDJ>GHUC/ G~PZWIr:37QSnV}V~Tyf_{\b|ca\&aMeACEHGELUF6&6<@BREH;?KcaYUSNLRX`YWUEDB4;7?EVm*O{P'$&)8DIG;.'.S[E'2}^RiGh:*ED[^VtToTjr\Vcqj`b"[Oa?CHIGELQ?/1) -#+'&7=DTHI==L>GOFO=^\WRPUSYd^PKIHHC062;EP{}7 +=S*# (>RG9?9/ @VJ3'j~~mUyGX1!FCujYUfZaXgYVzffubk'UR]ACDEFFKL8'&#@UERnX-9BCNEDC=L=FPFL:X\RRSZYc]]YKPPOI3616CLnL".),*-8A@;53/*%$8HH;R}~YmLC/#;Ec~Ya[`[b\YVpmgcr%RWXCCEFHFKG-+OA3;tn98@@QFEJAS?GRGR:S`W]\_b`W[[P]VNH8845BIf`" #&+/40/78/,+%!8NE>#B^aW;2.9NYW~v]q^q^Ys^luid{+O_UACEEFCE>''')',JeZ=!7AQ@EPEP?P^Ycdc]WOXd\cSDG>46.CF[s4!!,42-.7;8)-EKEP@THIULTAMbajebUUV`h[XUFIN2;-;FW}F%(4HPH=blQ49:1$f}wuZrF%.MjXee^^{]hculhf?GlVBDDEHFC1(- +)2%0@DEKHS>SFIUFLCHachZYXW`fbUOYPUO19*2GPuB'8Od`Uel<)51Uz}`qN&QuTpwaaoYm]tr`fie~EAqWEEGIHEG0! +#"6FEHQKTEQDERKMDEae^UR^^d\V[P`^KI3IIFOLU@NIHTKKDCe_ZTMkg\UXcW^^JH6:6+DGb<@es~|I0*3.2y}s|zaueeae_h`khr]:z}YFEFIIJI323()UkO*(.9AIDBKGOCNJKRCNHA^UT[Jg`WVbdZV[JNA5<0>I\.Ehpx}yxz{I'!,$#g~znlgt[qchYufwiii;oYEGIKLIPI;:GM[c^X[]acgklmmqpoqswuvwt{zy}zBKSG<69G[ZI@EUlwT(IFEFILMYgiikoprvwy}}Y-UM2)-88{}B/'8Xsi4(Imnnmnmlhhigeccecb__`a^^\^]\\\]^]]][\^^``bbceebFGGJLLZYOMNOOQRTTSUVYYYZ[[\\^^___^^^`abbcegffffgijiknnnonooqY(2`TGCA=KK9>V_xwHPoHIJMMN^lqopqrsuwzz}n4Ajymei`_vM@FGHJKKZghgilnoruwwz|}~~M;awWj~}|{yxwvtrpomlkhffebaaa_\[YWTKMMLNMK8" +  + + + + + + + + +  +  ,?QttaA!!"$&%$&'''&(&'&'(&%%#%$"$$%#!"##"HKKJJKI?+    + +     + +ASd|>AGRve5"++!$" .12,!! "! ! JJLMLLKD5  +    +  +  +3P\m|E?Wwz*11''1/ =AB)  KKNMKKID;%->5-:3:@C.5AC52=  (+ 3 !?Wf{~o{~(00&%-. #$EDN+   8WX=GCMZTZfdIL]IJLJKKKJA+1D3,@8FKL.9GG64F    kj(#$4S`u£|x3qq;S69G6*MQQ( + +NeeRMDRd^]feGG[JJKLONMKE2>S@9LAVXY9H[]CG_) + + + \[%"$>cÍUYbkswwx_p:{s3Ov-Ivj΀z7GuhLc[{tsJLNNOOUfkW`zL\ia|oHv~Soo  #& 5bR$AHMpMNUXSSZhqlkx^r0QK-.<:#2NUQmeO:FMGCHRssqPORRRSdnmYb|Naj_}rJxTsj   +$%  yFLIQY]dhbgnvl=77*,43$8UZ\lϸIY\DNHBF\oopMOOOORdnmV`|Sgk^~uKwTuh  #%+UKKHEJS^dda[^/1:8':VX`|֪KPICMD?AbnrqNNMQSUgpoU^SgiZ{MxSye + + +  +%%!ٺɒBJNMKQ[\WLG9$ ! >W[bx۳Z5G_LZN@qnvsMNOQQTgpnP^Zni\~}QuU|c  + + +$& _yFKLKLNKOK'%%EX[kAzqvʅ^^vjT[[;;*UzKYn^}x{LMNOQViqkDNbqt_^z__  %&A࢏f]kAjnǡnOOP7.Do[6^trPMMPRVjokIBiln~]xad  +%'!?渲7<3La^CVlǨMNOQRYlpmK>rclx^v[o   +')`QEMTQFHBs~ѳNMPOOYmpkN8|~`kvat^z' +  '&:Y[[UTG@ʎwzϴLMNPPYnrlO6z]tpcn`z-  +   ))˵٠aǍz|DZ¼uNPQPQ]qtlN+o`{}tmgmw7 & +! !/MOHnlZFZ}Q¾ƪÿmNNMNQ]qtlP$a^]rvbxv9   !!xkD\P|]p~álLNPRR_wytgYbs]XpYk~UF{ees, $!!6RazrViƬxļnKLMPS`rssvy|LZ?HKDG:rknyƳ½wKLNPRZH9:99<=<>=@=::<;;==?@BGLORUY_ciosx~оLKMOP^lnlllmnopsuuuwwvutsqpopnmmjhgeeb`__]\_x{}}~JLMKN\nrsru{}Յ{zvvtrrqpprqpoommmmkkihgheLKJNOK: + +  $',048:>CHNTX^chdLMLLMNH*  }ոo{LJJLJJG4 + E(%!KKLLIIH>% ټ{&#!! !!!!" !#LLKLKIE?-  ϭ2&$#""!!! !!""!!"!#""$MKLMJFGB5   4֥`+$#$$!$$""#! ######%'&KIJJIGEB:( Nζɻ#)'%$"###""#####!#%%&%&)KHHJIIFC?/ lɪᰡ<+($$$'&&$#$$%$#$%&&&),+KIHJJHGD?4# ⫗s(*(&'&&&&&&&&&'('&)*,..IHHIJFECA9(-ฤڬ7-,)(''&'''')(&'*-./123IIIHEFECA<0 =뾬ֽЫ\0-*)(''''('')*+,--0123HHGHHIGEC>4%Uó(,*+)))*+***+,-/223365GEHIFFDDB?7)k¶찯D0,**((*+++++./0344223GGHGGFIFCA<6-!Fة‘k0/-/.,-./01212342243DDDEGGECB@<7."`Īɼλ򹋦1.-.--.-0/0223310110CBDDECDB??<80$,{wټʶǼZ111/./0110111310110BABDEEFC@?=;4)  7ƺy0200010011331110121D?ACEDEECA>;7,#lͺqyqwnȽM5331121/1220100220?=@BDDDDB@=97.%!̯jrut~Uhj͟`9431121212111100/00@=AAEHGDC@>:62&RA*\qsrmaYWUkT#jj;=UG7322243122112100110-.?=>@CDEDCA><83* ,0$,Rfgeb_URU]hS + |bX5?BIL;2222201232332111221011/?<=>BDDA@BA>;4+!  !" !""!$$%$'&(6Q\\_b]ZXZXR^yY (܊o|Z:;AJOE7355356866544565565553311>=??BBCDCA@>950! "!!#"!""$$%#$$%%&*29GQUWYUTTLDEVWZeX9180>ךryM==AJTS<6799788768987677886676765 \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/readpgm.m b/SD-VBS/common/toolbox/toolbox_basic/affine/readpgm.m new file mode 100755 index 0000000..a5fd7f2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/readpgm.m @@ -0,0 +1,26 @@ +function img = pgmread(filename) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + + +fid = fopen(filename,'r'); +fscanf(fid, 'P5\n'); +cmt = '#'; +while findstr(cmt, '#'), + cmt = fgets(fid); + if length(findstr(cmt, '#')) ~= 1, + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end +end + +fgets(fid); + +%img = fscanf(fid,'%d',size); +%img = img'; + +img = fread(fid,[y,x],'uint8'); +img = img'; +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/simulation.m b/SD-VBS/common/toolbox/toolbox_basic/affine/simulation.m new file mode 100755 index 0000000..2186a6d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/simulation.m @@ -0,0 +1,42 @@ +clear + +figure(1);colormap(gray); + +%------------ Parameters -------------------------- +window_size_h = 40; +window_size = 2*window_size_h+1; +noise_level = 40/256; + +% define A and D +x_ext = -0.423; +ext = 1.232; +A = [ext+x_ext, 0.2534; 0.3423,ext]; + +D = [3,1]; + +%------------- compute image I and J --------------- +disp('generating I') +I_init = gen_feature_s(window_size); +[size_y,size_x] = size(I_init); + +%define image center +[center_x,center_y] = find_center(size_x,size_y); + +% adding noise to image I +I = I_init+noise_level*rand(size_y,size_x); +% make sure all intensities are positive +I = I.*(I>0); + +disp('computing J') +J_init = compute_J(A,D,I_init,[center_x,center_y],[window_size_h,window_size_h]); +J = J_init+noise_level*rand(size_y,size_x); +J = J.*(J>0); + + +%------------- compute A and residue ---------------- +c = [center_x,center_y]; +num_iter = 8; w = 9;win_h = [window_size_h,window_size_h]; + +fig_disp = 1; +[Ac,Dc,mask] = compute_AD_disp(I,J,c,c,win_h,num_iter,w,fig_disp); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/sports1_11_28.jpeg b/SD-VBS/common/toolbox/toolbox_basic/affine/sports1_11_28.jpeg new file mode 100755 index 0000000..39ebed5 Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/affine/sports1_11_28.jpeg differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/affine/test_affine.m b/SD-VBS/common/toolbox/toolbox_basic/affine/test_affine.m new file mode 100755 index 0000000..41b48b9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affine/test_affine.m @@ -0,0 +1,33 @@ +%%% This is a test program for Affine tracker %%%% + +disp(sprintf('This is a test program of Affine tracker')); + +%% read in images + +disp(sprintf('read in images')); +I = readpgm('pan.0.pgm'); +J = readpgm('pan.1.pgm'); + +figure(1); im(I); colormap(gray); +figure(2); im(J); colormap(gray); + + +figure(1);disp(sprintf('click on the center of a image window')); +c = round(ginput(1)); + +%% compute the displacement of that image window +disp(sprintf('computing...')); + +win_hsize_temp = [8,8]; +w = 3; +num_iter = 6; + +disp_flag = 1; + +win_h = win_hsize_temp + [w,w]; +if disp_flag == 1, + figure_id = 3; + [A,D,mask] = compute_AD_disp(I,J,c,c,win_h,num_iter,w,figure_id); +else + [A,D,mask] = compute_AD(I,J,c,c,win_h,num_iter,w); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/affinityic.c b/SD-VBS/common/toolbox/toolbox_basic/affinityic.c new file mode 100755 index 0000000..e48762a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/affinityic.c @@ -0,0 +1,186 @@ +/*================================================================ +* function w = affinityic(emag,ephase,pi,pj,sigma) +* Input: +* emag = edge strength at each pixel +* ephase = edge phase at each pixel +* [pi,pj] = index pair representation for MALTAB sparse matrices +* sigma = sigma for IC energy +* Output: +* w = affinity with IC at [pi,pj] +* + +% test sequence +f = synimg(10); +[i,j] = cimgnbmap(size(f),2); +[ex,ey,egx,egy] = quadedgep(f); +a = affinityic(ex,ey,egx,egy,i,j) +show_dist_w(f,a); + +* Jianbo Shi, Stella X. Yu, Nov 19, 2001. +*=================================================================*/ + +# include "mex.h" +# include "math.h" + +void mexFunction( + int nargout, + mxArray *out[], + int nargin, + const mxArray *in[] +) +{ + /* declare variables */ + int nr, nc, np, total; + int i, j, k, ix, iy, jx, jy, ii, jj, iip1, jjp1, iip2, jjp2, step; + double sigma, di, dj, a, z, maxori, phase1, phase2, slope; + int *ir, *jc; + unsigned long *pi, *pj; + double *emag, *ephase, *w; + + /* check argument */ + if (nargin<4) { + mexErrMsgTxt("Four input arguments required"); + } + if (nargout>1) { + mexErrMsgTxt("Too many output arguments"); + } + + /* get edgel information */ + nr = mxGetM(in[0]); + nc = mxGetN(in[0]); + if ( nr*nc ==0 || nr != mxGetM(in[1]) || nc != mxGetN(in[1]) ) { + mexErrMsgTxt("Edge magnitude and phase shall be of the same image size"); + } + emag = mxGetPr(in[0]); + ephase = mxGetPr(in[1]); + np = nr * nc; + + /* get new index pair */ + if (!mxIsUint32(in[2]) | !mxIsUint32(in[3])) { + mexErrMsgTxt("Index pair shall be of type UINT32"); + } + if (mxGetM(in[3]) * mxGetN(in[3]) != np + 1) { + mexErrMsgTxt("Wrong index representation"); + } + pi = mxGetData(in[2]); + pj = mxGetData(in[3]); + + /* create output */ + out[0] = mxCreateSparse(np,np,pj[np],mxREAL); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough memory for the output matrix"); + } + w = mxGetPr(out[0]); + ir = mxGetIr(out[0]); + jc = mxGetJc(out[0]); + + /* find my sigma */ + if (nargin<5) { + sigma = 0; + for (k=0; ksigma) { sigma = emag[k]; } + } + sigma = sigma / 10; + printf("sigma = %6.5f",sigma); + } else { + sigma = mxGetScalar(in[4]); + } + a = 1.0/ (sigma); + + /* computation */ + total = 0; + for (j=0; j= abs(dj)) { + slope = dj / di; + step = (iy>=jy) ? 1 : -1; + + iip1 = jy; + jjp1 = jx; + + + for (ii=0;ii maxori){ + maxori = z; + } + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + + /* sample in j direction */ + } else { + slope = di / dj; + step = (ix>=jx) ? 1: -1; + + jjp1 = jx; + iip1 = jy; + + + for (jj=0;jj maxori){ + maxori = z; + } + + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + } + + maxori = 0.5 * maxori*a; + maxori = exp(-maxori*maxori); + } + ir[total] = i; + + w[total] = maxori + 0.005; + total = total + 1; + + } /* i */ + } /* j */ + + jc[np] = total; +} diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib/TOOLBOX_calib.tar b/SD-VBS/common/toolbox/toolbox_basic/calib/TOOLBOX_calib.tar new file mode 100755 index 0000000..92ab3a0 Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/calib/TOOLBOX_calib.tar differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Distor2Calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Distor2Calib.m new file mode 100755 index 0000000..a82f583 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Distor2Calib.m @@ -0,0 +1,391 @@ +function [fc_2,Rc_2,Tc_2,H_2,distance,V_vert,V_hori,x_all_c,V_hori_pix,V_vert_pix,V_diag1_pix,V_diag2_pix]=Distor2Calib(k_dist,grid_pts_centered,n_sq_x,n_sq_y,Np,W,L,Xgrid_2,f_ini,N_iter,two_focal); + +% Computes the calibration parameters knowing the +% distortion factor k_dist + +% grid_pts_centered are the grid point coordinates after substraction of +% the optical center. + +% can give an optional guess for the focal length f_ini (can set to []) +% can provide the number of iterations for the Iterative Vanishing Point Algorithm + +% if the focal length is known perfectly, then, there is no need to iterate, +% and therefore, one can fix: N_iter = 0; + +% California Institute of Technology +% (c) Jean-Yves Bouguet - October 7th, 1997 + + + +%keyboard; + +if exist('two_focal'), + if isempty(two_focal), + two_focal=0; + end; +else + two_focal = 0; +end; + + +if exist('N_iter'), + if ~isempty(N_iter), + disp('Use number of iterations provided'); + else + N_iter = 10; + end; +else + N_iter = 10; +end; + +if exist('f_ini'), + if ~isempty(f_ini), + disp('Use focal provided'); + if length(f_ini)<2, f_ini=[f_ini;f_ini]; end; + fc_2 = f_ini; + x_all_c = [grid_pts_centered(1,:)/fc_2(1);grid_pts_centered(2,:)/fc_2(2)]; + x_all_c = comp_distortion(x_all_c,k_dist); % we can this time!!! + else + fc_2 = [1;1]; + x_all_c = grid_pts_centered; + end; +else + fc_2 = [1;1]; + x_all_c = grid_pts_centered; +end; + + +dX = W/n_sq_x; +dY = L/n_sq_y; + + +N_x = n_sq_x+1; +N_y = n_sq_y+1; + + +x_grid = zeros(N_x,N_y); +y_grid = zeros(N_x,N_y); + + + + + +%%% Computation of the four vanishing points in pixels + + + x_grid(:) = grid_pts_centered(1,:); + y_grid(:) = grid_pts_centered(2,:); + + for k=1:n_sq_x+1, + [U,S,V] = svd([x_grid(k,:);y_grid(k,:);ones(1,n_sq_y+1)]); + vert(:,k) = U(:,3); + end; + + for k=1:n_sq_y+1, + [U,S,V] = svd([x_grid(:,k)';y_grid(:,k)';ones(1,n_sq_x+1)]); + hori(:,k) = U(:,3); + end; + + % 2 principle Vanishing points: + [U,S,V] = svd(vert); + V_vert = U(:,3); + [U,S,V] = svd(hori); + V_hori = U(:,3); + + + + % Square warping: + + + vert_first = vert(:,1) - dot(V_vert,vert(:,1))/dot(V_vert,V_vert) * V_vert; + vert_last = vert(:,n_sq_x+1) - dot(V_vert,vert(:,n_sq_x+1))/dot(V_vert,V_vert) * V_vert; + + hori_first = hori(:,1) - dot(V_hori,hori(:,1))/dot(V_hori,V_hori) * V_hori; + hori_last = hori(:,n_sq_y+1) - dot(V_hori,hori(:,n_sq_y+1))/dot(V_hori,V_hori) * V_hori; + + + x1 = cross(hori_first,vert_first); + x2 = cross(hori_first,vert_last); + x3 = cross(hori_last,vert_last); + x4 = cross(hori_last,vert_first); + + x1 = x1/x1(3); + x2 = x2/x2(3); + x3 = x3/x3(3); + x4 = x4/x4(3); + + + + [square] = Rectangle2Square([x1 x2 x3 x4],W,L); + + y1 = square(:,1); + y2 = square(:,2); + y3 = square(:,3); + y4 = square(:,4); + + H2 = cross(V_vert,V_hori); + + V_diag1 = cross(cross(y1,y3),H2); + V_diag2 = cross(cross(y2,y4),H2); + + V_diag1 = V_diag1 / norm(V_diag1); + V_diag2 = V_diag2 / norm(V_diag2); + + V_hori_pix = V_hori; + V_vert_pix = V_vert; + V_diag1_pix = V_diag1; + V_diag2_pix = V_diag2; + + +% end of computation of the vanishing points in pixels. + + + + + + + + +if two_focal, % only if we attempt to estimate two focals... + % Use diagonal lines also to add two extra vanishing points (?) + N_min = min(N_x,N_y); + + if N_min < 2, + use_diag = 0; + two_focal = 0; + disp('Cannot estimate two focals (no existing diagonals)'); + else + use_diag = 1; + Delta_N = abs(N_x-N_y); + N_extra = round((N_min - Delta_N - 1)/2); + diag_list = -N_extra:Delta_N+N_extra; + N_diag = length(diag_list); + diag_1 = zeros(3,N_diag); + diag_2 = zeros(3,N_diag); + end; +else + % Give up the use of the diagonals (so far) + % it seems that the error is increased + use_diag = 0; +end; + + + +% The vertical lines: vert, Horizontal lines: hori +vert = zeros(3,n_sq_x+1); +hori = zeros(3,n_sq_y+1); + +for counter_k = 1:N_iter, % the Iterative Vanishing Points Algorithm to + % estimate the focal length accurately + + x_grid(:) = x_all_c(1,:); + y_grid(:) = x_all_c(2,:); + + for k=1:n_sq_x+1, + [U,S,V] = svd([x_grid(k,:);y_grid(k,:);ones(1,n_sq_y+1)]); + vert(:,k) = U(:,3); + end; + + for k=1:n_sq_y+1, + [U,S,V] = svd([x_grid(:,k)';y_grid(:,k)';ones(1,n_sq_x+1)]); + hori(:,k) = U(:,3); + end; + + % 2 principle Vanishing points: + [U,S,V] = svd(vert); + V_vert = U(:,3); + [U,S,V] = svd(hori); + V_hori = U(:,3); + + + + % Square warping: + + + vert_first = vert(:,1) - dot(V_vert,vert(:,1))/dot(V_vert,V_vert) * V_vert; + vert_last = vert(:,n_sq_x+1) - dot(V_vert,vert(:,n_sq_x+1))/dot(V_vert,V_vert) * V_vert; + + hori_first = hori(:,1) - dot(V_hori,hori(:,1))/dot(V_hori,V_hori) * V_hori; + hori_last = hori(:,n_sq_y+1) - dot(V_hori,hori(:,n_sq_y+1))/dot(V_hori,V_hori) * V_hori; + + + x1 = cross(hori_first,vert_first); + x2 = cross(hori_first,vert_last); + x3 = cross(hori_last,vert_last); + x4 = cross(hori_last,vert_first); + + x1 = x1/x1(3); + x2 = x2/x2(3); + x3 = x3/x3(3); + x4 = x4/x4(3); + + + + [square] = Rectangle2Square([x1 x2 x3 x4],W,L); + + y1 = square(:,1); + y2 = square(:,2); + y3 = square(:,3); + y4 = square(:,4); + + H2 = cross(V_vert,V_hori); + + V_diag1 = cross(cross(y1,y3),H2); + V_diag2 = cross(cross(y2,y4),H2); + + V_diag1 = V_diag1 / norm(V_diag1); + V_diag2 = V_diag2 / norm(V_diag2); + + + + + % Estimation of the focal length, and normalization: + + % Compute the ellipsis of (1/f^2) positions: + % a * (1/fx)^2 + b * (1/fx)^2 = -c + + + a1 = V_hori(1); + b1 = V_hori(2); + c1 = V_hori(3); + + a2 = V_vert(1); + b2 = V_vert(2); + c2 = V_vert(3); + + a3 = V_diag1(1); + b3 = V_diag1(2); + c3 = V_diag1(3); + + a4 = V_diag2(1); + b4 = V_diag2(2); + c4 = V_diag2(3); + + + if two_focal, + + + A = [a1*a2 b1*b2;a3*a4 b3*b4]; + b = -[c1*c2;c3*c4]; + + f = sqrt(abs(1./(inv(A)*b))); + + else + + f = sqrt(abs(-(c1*c2*(a1*a2 + b1*b2) + c3*c4*(a3*a4 + b3*b4))/(c1^2*c2^2 + c3^2*c4^2))); + + f = [f;f]; + + end; + + + + % REMARK: + % if both a and b are small, the calibration is impossible. + % if one of them is small, only the other focal length is observable + % if none is small, both focals are observable + + + fc_2 = fc_2 .* f; + + + % DEBUG PART: fix focal to 500... + %fc_2= [500;500]; disp('Line 293 to be earased in Distor2Calib.m'); + + + % end of focal compensation + + % normalize by the current focal: + + x_all = [grid_pts_centered(1,:)/fc_2(1);grid_pts_centered(2,:)/fc_2(2)]; + + % Compensate by the distortion factor: + + x_all_c = comp_distortion(x_all,k_dist); + +end; + +% At that point, we hope that the distortion is gone... + +x_grid(:) = x_all_c(1,:); +y_grid(:) = x_all_c(2,:); + +for k=1:n_sq_x+1, + [U,S,V] = svd([x_grid(k,:);y_grid(k,:);ones(1,n_sq_y+1)]); + vert(:,k) = U(:,3); +end; + +for k=1:n_sq_y+1, + [U,S,V] = svd([x_grid(:,k)';y_grid(:,k)';ones(1,n_sq_x+1)]); + hori(:,k) = U(:,3); +end; + +% Vanishing points: +[U,S,V] = svd(vert); +V_vert = U(:,3); +[U,S,V] = svd(hori); +V_hori = U(:,3); + +% Horizon: + +H_2 = cross(V_vert,V_hori); + +% H_2 = cross(V_vert,V_hori); + +% pick a plane in front of the camera (positive depth) +if H_2(3) < 0, H_2 = -H_2; end; + + +% Rotation matrix: + +if V_hori(1) < 0, V_hori = -V_hori; end; + +V_hori = V_hori/norm(V_hori); +H_2 = H_2/norm(H_2); + +V_hori = V_hori - dot(V_hori,H_2)*H_2; + +Rc_2 = [V_hori cross(H_2,V_hori) H_2]; + +Rc_2 = Rc_2 / det(Rc_2); + +%omc_2 = rodrigues(Rc_2); + +%Rc_2 = rodrigues(omc_2); + +% Find the distance of the plane for translation vector: + +xc_2 = [x_all_c;ones(1,Np)]; + +Zc_2 = 1./sum(xc_2 .* (Rc_2(:,3)*ones(1,Np))); + +Xo_2 = [sum(xc_2 .* (Rc_2(:,1)*ones(1,Np))).*Zc_2 ; sum(xc_2 .* (Rc_2(:,2)*ones(1,Np))).*Zc_2]; + +XXo_2 = Xo_2 - mean(Xo_2')'*ones(1,Np); + +distance_x = norm(Xgrid_2(1,:))/norm(XXo_2(1,:)); +distance_y = norm(Xgrid_2(2,:))/norm(XXo_2(2,:)); + + +distance = sum(sum(XXo_2(1:2,:).*Xgrid_2(1:2,:)))/sum(sum(XXo_2(1:2,:).^2)); + +alpha = abs(distance_x - distance_y)/distance; + +if (alpha>0.1)&~two_focal, + disp('Should use two focals in x and y...'); +end; + +% Deduce the translation vector: + +Tc_2 = distance * H_2; + + + + + +return; + + V_hori_pix/V_hori_pix(3) + V_vert_pix/V_vert_pix(3) + V_diag1_pix/V_diag1_pix(3) + V_diag2_pix/V_diag2_pix(3) diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Multi_Calib_oulu.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Multi_Calib_oulu.m new file mode 100755 index 0000000..62ca9ae --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Multi_Calib_oulu.m @@ -0,0 +1,12 @@ + +% enter image names, numbers, ... +data_calib; + +%read images from files +ima_read_calib; + +click_calib; + +%go_calib; % the original version + +go_calib_optim; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Rectangle2Square.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Rectangle2Square.m new file mode 100755 index 0000000..a6bbbe5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/Rectangle2Square.m @@ -0,0 +1,19 @@ +function [square] = Rectangle2Square(rectangle,L,W); + +% Generate the square from a rectangle of known segment lengths +% from pt1 to pt2 : L +% from pt2 to pt3 : W + +[u_hori,u_vert] = UnWarpPlane(rectangle); + +coeff_x = sqrt(W/L); +coeff_y = 1/coeff_x; + +x_coord = [ 0 coeff_x coeff_x 0]; +y_coord = [ 0 0 coeff_y coeff_y]; + + +square = rectangle(:,1) * ones(1,4) + u_hori*x_coord + u_vert*y_coord; +square = square ./ (ones(3,1)*square(3,:)); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/UnWarpPlane.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/UnWarpPlane.m new file mode 100755 index 0000000..8addf52 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/UnWarpPlane.m @@ -0,0 +1,54 @@ +function [u_hori,u_vert] = UnWarpPlane(x1,x2,x3,x4); + +% Recovers the two 3D directions of the rectangular patch x1x2x3x4 +% x1 is the origin point, ie any point of planar coordinate (x,y) on the +% rectangular patch will be projected on the image plane at: +% x1 + x * u_hori + y * u_vert +% +% Note: u_hori and u_vert are also the two vanishing points. + + +if nargin < 4, + + x4 = x1(:,4); + x3 = x1(:,3); + x2 = x1(:,2); + x1 = x1(:,1); + +end; + + +% Image Projection: +L1 = cross(x1,x2); +L2 = cross(x4,x3); +L3 = cross(x2,x3); +L4 = cross(x1,x4); + +% Vanishing point: +V1 = cross(L1,L2); +V2 = cross(L3,L4); + +% Horizon line: +H = cross(V1,V2); + +if H(3) < 0, H = -H; end; + + +H = H / norm(H); + + +X1 = x1 / dot(H,x1); +X2 = x2 / dot(H,x2); +X3 = x3 / dot(H,x3); +X4 = x4 / dot(H,x4); + +scale = X1(3); + +X1 = X1/scale; +X2 = X2/scale; +X3 = X3/scale; +X4 = X4/scale; + + +u_hori = X2 - X1; +u_vert = X4 - X1; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/add_suppress.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/add_suppress.m new file mode 100755 index 0000000..b9bcc57 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/add_suppress.m @@ -0,0 +1,91 @@ + check_active_images; + + +fprintf(1,'\nThis function is useful to select a subset of images to calibrate\n'); + + fprintf(1,'\nThere are currently %d active images selected for calibration (out of %d):\n',length(ind_active),n_ima); + + if ~isempty(ind_active), + + for ii = 1:length(ind_active)-2, + + fprintf(1,'%d, ',ind_active(ii)); + + end; + + fprintf(1,'%d and %d.',ind_active(end-1),ind_active(end)); + + end; + + fprintf(1,'\n'); + + +fprintf(1,'\nDo you want to suppress or add images from that list?\n'); + +choice = 2; + +while (choice~=0)&(choice~=1), + choice = input('For suppressing images enter 0, for adding images enter 1 ([]=no change): '); + if isempty(choice), + fprintf(1,'No change applied to the list of active images.\n'); + return; + end; + if (choice~=0)&(choice~=1), + disp('Bad entry. Try again.'); + end; +end; + + +if choice, + + ima_numbers = input('Number(s) of image(s) to add ([] = all images) = '); + +if isempty(ima_numbers), + fprintf(1,'All %d images are now active\n',n_ima); + ima_proc = 1:n_ima; + else + ima_proc = ima_numbers; + end; + +else + + + ima_numbers = input('Number(s) of image(s) to suppress ([] = no image) = '); + + if isempty(ima_numbers), + fprintf(1,'No image has been suppressed. No modication of the list of active images.\n',n_ima); + ima_proc = []; + else + ima_proc = ima_numbers; + end; + +end; + +if ~isempty(ima_proc), + + active_images(ima_proc) = choice * ones(1,length(ima_proc)); + +end; + + + check_active_images; + + + fprintf(1,'\nThere is now a total of %d active images for calibration:\n',length(ind_active)); + + if ~isempty(ind_active), + + for ii = 1:length(ind_active)-2, + + fprintf(1,'%d, ',ind_active(ii)); + + end; + + fprintf(1,'%d and %d.',ind_active(end-1),ind_active(end)); + + end; + + fprintf(1,'\n\nYou may now run ''Calibration'' to recalibrate based on this new set of images.\n'); + + + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/analyse_error.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/analyse_error.m new file mode 100755 index 0000000..5bfa3b5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/analyse_error.m @@ -0,0 +1,104 @@ +% Color code for each image: + +check_active_images; + +if ~exist(['ex_' num2str(ind_active(1)) ]), + fprintf(1,'Need to calibrate before analysing reprojection error. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + + +if ~exist('no_grid'), + no_grid = 0; +end; + +colors = 'brgkcm'; + + +figure(5); + +for kk = 1:n_ima, + if active_images(kk) & eval(['~isnan(y_' num2str(kk) '(1,1))']), + eval(['plot(ex_' num2str(kk) '(1,:),ex_' num2str(kk) '(2,:),''' colors(rem(kk-1,6)+1) '+'');']); + hold on; + end; +end; +hold off; +axis('equal'); +if 1, %~no_grid, + title('Reprojection error (in pixel) - To exit: right button'); +else + title('Reprojection error (in pixel)'); +end; +xlabel('x'); +ylabel('y'); + +set(5,'Name','error','NumberTitle','off'); + + + +%err_std = std(ex')'; + +%fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); + +b = 1; + +while b==1, + +[xp,yp,b] = ginput3(1); + +if b==1, +ddd = (ex(1,:)-xp).^2 + (ex(2,:)-yp).^2; + +[mind,indmin] = min(ddd); + + +done = 0; +kk_ima = 1; +while (~done)&(kk_ima<=n_ima), + %fprintf(1,'%d...',kk_ima); + eval(['ex_kk = ex_' num2str(kk_ima) ';']); + sol_kk = find((ex_kk(1,:) == ex(1,indmin))&(ex_kk(2,:) == ex(2,indmin))); + if isempty(sol_kk), + kk_ima = kk_ima + 1; + else + done = 1; + end; +end; + +if ~no_grid, + +eval(['n_sq_x = n_sq_x_' num2str(kk_ima) ';']); +eval(['n_sq_y = n_sq_y_' num2str(kk_ima) ';']); + +Nx = n_sq_x+1; +Ny = n_sq_y+1; + +y1 = floor((sol_kk-1)./Nx); +x1 = sol_kk - 1 - Nx*y1; %rem(sol_kk-1,Nx); + +y1 = (n_sq_y+1) - y1; +x1 = x1 + 1; + +fprintf(1,'\nSelected image: %d\nSelected point: (col,row)=(%d,%d)\nNcol=%d, Nrow=%d\n',[kk_ima x1 y1 Nx Ny]); +fprintf(1,'Pixel error = (%3.5f,%3.5f)\n',[ex(1,indmin) ex(2,indmin)]); + +else + + eval(['x_kk = x_' num2str(kk_ima) ';']); + + xpt = x_kk(:,sol_kk); + +fprintf(1,'\nSelected image: %d\nImage coordinates (in pixel): (%3.2f,%3.2f)\n',[kk_ima xpt']); +fprintf(1,'Pixel error = (%3.5f,%3.5f)\n',[ex(1,indmin) ex(2,indmin)]); + + +end; + + +end; + +end; + +disp('done'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib.m new file mode 100755 index 0000000..5b0fdac --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib.m @@ -0,0 +1,74 @@ +if ~exist('instructions'), instructions = 1; end; + +if instructions, + +fprintf(1,'\n'); +fprintf(1,'*----------------------------------------------------------------------------------------------------*\n'); +fprintf(1,'| Main Calibration toolbox (2D and 3D rigs) |\n'); +fprintf(1,'| (c) Jean-Yves Bouguet - September 9th, 1999 |\n'); +fprintf(1,'*----------------------------------------------------------------------------------------------------*\n\n\n'); + +fprintf(1,'CLICK ON:\n\n'); + +fprintf(1,'2D: To perform camera calibration from multiple views of a 2D planar grid. \n'); +fprintf(1,' Set default size of grid (in dX_default and dY_default) in click_calib.m.\n'); +fprintf(1,'3D: To perform camera calibration from multiple views of a 3D grid corner. \n'); +fprintf(1,' Set default size of grids (in dX_default and dY_default) in click_calib3D.m.\n'); +fprintf(1,'Exit: To close the calibration tool. \n'); + +end; + +instructions = 0; + +fig_number = 1; + +n_row = 1; +n_col = 3; + +string_list = cell(n_row,n_col); +callback_list = cell(n_row,n_col); + +x_size = 40; +y_size = 20; + +title_figure = 'Calibration tool'; + +string_list{1,1} = '2D rig'; +string_list{1,2} = '3D rig'; +string_list{1,3} = 'Exit'; + +callback_list{1,1} = 'calib_gui;'; +callback_list{1,2} = 'calib3D_gui;'; +callback_list{1,3} = ['disp(''Bye. To run again, type calib.''); close(' num2str(fig_number) ');']; + + +figure(fig_number); clf; +pos = get(fig_number,'Position'); + +fig_size_x = x_size*n_col+(n_col+1)*2; +fig_size_y = y_size*n_row+(n_row+1)*2; + +set(fig_number,'Units','points', ... + 'BackingStore','off', ... + 'Color',[0.8 0.8 0.8], ... + 'MenuBar','none', ... + 'Resize','off', ... + 'Name',title_figure, ... +'Position',[pos(1) pos(2) fig_size_x fig_size_y], ... +'NumberTitle','off'); + + +for i=n_row:-1:1, + for j = n_col:-1:1, + if (~isempty(callback_list{i,j}))&(~isempty(string_list{i,j})), + uicontrol('Parent',fig_number, ... + 'Units','points', ... + 'Callback',callback_list{i,j}, ... + 'ListboxTop',0, ... + 'Position',[(2+(j-1)*(x_size+2)) (fig_size_y - i*(2+y_size)) x_size y_size], ... + 'String',string_list{i,j}, ... + 'Tag','Pushbutton1'); + end; + end; +end; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib3D_gui.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib3D_gui.m new file mode 100755 index 0000000..ff24f6b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib3D_gui.m @@ -0,0 +1,115 @@ +if ~exist('instructions3D'), instructions3D = 1; end; + +if instructions3D, + +fprintf(1,'\n'); +fprintf(1,'*----------------------------------------------------------------------------------------------------*\n'); +fprintf(1,'| Canera calibration from multiple images of the Intel 3D calibration rig |\n'); +fprintf(1,'| (c) Jean-Yves Bouguet - September 2nd, 1999 |\n'); +fprintf(1,'*----------------------------------------------------------------------------------------------------*\n\n\n'); + +fprintf(1,'LIST OF CALIBRATION COMMANDS (to be executed from 1 to 5):\n\n'); + +fprintf(1,'1- Image names: Lets the user enter the file names of the calibration images (max = 30 images).\n'); +fprintf(1,' It includes basename, image type (''tif'', ''bmp'' or ''ras''), numbering scheme.\n'); +fprintf(1,' Automatically launchs the next step (Read images).\n'); +fprintf(1,'2- Read images: Reads in the calibration images from files.\n'); +fprintf(1,' Does not automatically launch the next step (Extract grid corners).\n'); +fprintf(1,'3- Extract grid corners: Extracts the grid corners from the image.\n'); +fprintf(1,' Based six maual clicks per image.\n'); +fprintf(1,' The calibration data is saved under ''calib_data.mat''.\n'); +fprintf(1,' Automatically launchs the next step (Run calibration).\n'); +fprintf(1,'4- Run calibration: Main calibration procedure.\n'); +fprintf(1,' Optimization of intrinsic and extrinsic parameters to minimize\n'); +fprintf(1,' the reprojection error (in the least squares sense.\n'); +fprintf(1,' Estimated parameters: 2 focal lengths, principal point,\n'); +fprintf(1,' radial (2 coeff. -> 4 degree model) and tangential (2 coeff.) distortion,\n'); +fprintf(1,' and extrinsic parameters (6 parameters per image).\n'); +fprintf(1,' The final solution is saved under ''Calib_Results.mat''.\n'); +fprintf(1,' For a description of the intrinsic camera model, refer to the reference:\n'); +fprintf(1,' "A Four-step Camera Calibration Procedure with implicit Image Correction"\n'); +fprintf(1,' Janne Heikkila and Olli Silven, Infotech Oulu and Department of EE\n'); +fprintf(1,' University of Oulu, Appeared in CVPR''97, Puerto Rico.\n'); +fprintf(1,' Visit http://www.ee.oulu.fi/~jth/calibr/Calibration.html\n'); +fprintf(1,' Automatically launchs the next step (Graphic out).\n'); +fprintf(1,'5- Graphic out: Generates the graphical output associated to the current calibration solution.\n'); +fprintf(1,' It shows the 3D locations of the grids, and reprojects the 3D patterns on the\n'); +fprintf(1,' original calibration images.\n'); +fprintf(1,'6- sol. with center: Lets the user select the calibration solution with computed principal point.\n'); +fprintf(1,' This is the default case (solution retained after Run calibration).\n'); +fprintf(1,' Automatically (re)generates the graphical output associated to that solution.\n'); +fprintf(1,'7- sol. without center: Lets the users select the calibration solution without computed principal point.\n'); +fprintf(1,' In that case, the principal point is assumed at the center of the image.\n'); +fprintf(1,' Automatically generates the graphical output associated to that solution.\n'); +fprintf(1,' This option is sometimes useful when the principal point is difficult to\n'); +fprintf(1,' estimate (in particular when the camera field of view is small).\n'); +fprintf(1,'8- Back to main: Goes back to the main calbration toolbox window.\n\n\n'); + +end; + +instructions3D = 0; + +global X_1 x_1 X_2 x_2 X_3 x_3 X_4 x_4 X_5 x_5 X_6 x_6 X_7 x_7 X_8 x_8 X_9 x_9 X_10 x_10 X_11 x_11 X_12 x_12 X_13 x_13 X_14 x_14 X_15 x_15 X_16 x_16 X_17 x_17 X_18 x_18 X_19 x_19 X_20 x_20 X_21 x_21 X_22 x_22 X_23 x_23 X_24 x_24 X_25 x_25 X_26 x_26 X_27 x_27 X_28 x_28 X_29 x_29 X_30 x_30 + + +fig_number = 1; + +n_row = 2; +n_col = 4; + +string_list = cell(n_row,n_col); +callback_list = cell(n_row,n_col); + +x_size = 85; +y_size = 20; + +title_figure = 'Camera calibration tool (3D rig)'; + +string_list{1,1} = 'Image names'; +string_list{1,2} = 'Read images'; +string_list{1,3} = 'Extract grid corners'; +string_list{1,4} = 'Run calibration'; +string_list{2,1} = 'Graphic out'; +string_list{2,2} = 'sol. with center'; +string_list{2,3} = 'sol. without center'; +string_list{2,4} = 'Back to main'; + +callback_list{1,1} = 'data_calib;'; +callback_list{1,2} = 'ima_read_calib;'; +callback_list{1,3} = 'click_calib3D;'; +callback_list{1,4} = 'go_calib_optim3D;'; +callback_list{2,1} = 'graphout_calib3D;'; +callback_list{2,2} = 'select_sol_with_center3D;'; +callback_list{2,3} = 'select_sol_no_center3D;'; +callback_list{2,4} = 'calib;'; + + +figure(fig_number); clf; +pos = get(fig_number,'Position'); + +fig_size_x = x_size*n_col+(n_col+1)*2; +fig_size_y = y_size*n_row+(n_row+1)*2; + +set(fig_number,'Units','points', ... + 'BackingStore','off', ... + 'Color',[0.8 0.8 0.8], ... + 'MenuBar','none', ... + 'Resize','off', ... + 'Name',title_figure, ... +'Position',[pos(1) pos(2) fig_size_x fig_size_y], ... +'NumberTitle','off'); + + +for i=n_row:-1:1, + for j = n_col:-1:1, + if (~isempty(callback_list{i,j}))&(~isempty(string_list{i,j})), + uicontrol('Parent',fig_number, ... + 'Units','points', ... + 'Callback',callback_list{i,j}, ... + 'ListboxTop',0, ... + 'Position',[(2+(j-1)*(x_size+2)) (fig_size_y - i*(2+y_size)) x_size y_size], ... + 'String',string_list{i,j}, ... + 'Tag','Pushbutton1'); + end; + end; +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib_gui.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib_gui.m new file mode 100755 index 0000000..62a45dd --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/calib_gui.m @@ -0,0 +1,81 @@ +fig_number = 1; + +n_row = 5; +n_col = 4; + +string_list = cell(n_row,n_col); +callback_list = cell(n_row,n_col); + +x_size = 85; +y_size = 20; + +title_figure = 'Camera calibration tool (2D rig)'; + +string_list{1,1} = 'Image names'; +string_list{1,2} = 'Read images'; +string_list{1,3} = 'Extract grid corners'; +%string_list{1,4} = 'Initialization'; +string_list{1,4} = 'Calibration'; +string_list{2,1} = 'Show Extrinsic'; +string_list{2,2} = 'Reproject on images'; +string_list{2,3} = 'Analyse error'; +string_list{2,4} = 'Recomp. corners'; +string_list{3,1} = 'Add/Suppress images'; +string_list{3,2} = 'Save'; +string_list{3,3} = 'Load'; +string_list{3,4} = 'Exit'; + +string_list{5,1} = 'Comp. Extrinsic'; +string_list{5,2} = 'Undistort image'; + + +callback_list{1,1} = 'data_calib;'; +callback_list{1,2} = 'ima_read_calib;'; +callback_list{1,3} = 'click_calib;'; +%callback_list{1,4} = 'init_calib_param;'; +callback_list{1,4} = 'go_calib_optim;'; +callback_list{2,1} = 'ext_calib;'; +callback_list{2,2} = 'reproject_calib;'; +callback_list{2,3} = 'analyse_error;'; +callback_list{2,4} = 'recomp_corner_calib;'; +callback_list{3,1} = 'add_suppress;'; +callback_list{3,2} = 'saving_calib;'; +callback_list{3,3} = 'loading_calib;'; +callback_list{3,4} = ['disp(''Bye. To run again, type calib_gui.''); close(' num2str(fig_number) ');']; + +callback_list{5,1} = 'extrinsic_computation;'; +callback_list{5,2} = 'undistort_image;'; + + +figure(fig_number); clf; +pos = get(fig_number,'Position'); + +fig_size_x = x_size*n_col+(n_col+1)*2; +fig_size_y = y_size*n_row+(n_row+1)*2; + +set(fig_number,'Units','points', ... + 'BackingStore','off', ... + 'Color',[0.8 0.8 0.8], ... + 'MenuBar','none', ... + 'Resize','off', ... + 'Name',title_figure, ... +'Position',[pos(1) pos(2) fig_size_x fig_size_y], ... +'NumberTitle','off'); %,'WindowButtonMotionFcn',['figure(' num2str(fig_number) ');']); + + +for i=n_row:-1:1, + for j = n_col:-1:1, + if (~isempty(callback_list{i,j}))&(~isempty(string_list{i,j})), + uicontrol('Parent',fig_number, ... + 'Units','points', ... + 'Callback',callback_list{i,j}, ... + 'ListboxTop',0, ... + 'Position',[(2+(j-1)*(x_size+2)) (fig_size_y - i*(2+y_size)) x_size y_size], ... + 'String',string_list{i,j}, ... + 'Tag','Pushbutton1'); + end; + end; +end; + + +clear callback_list string_list fig_number fig_size_x fig_size_y i j n_col n_row pos string_list title_figure x_size y_size diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_active_images.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_active_images.m new file mode 100755 index 0000000..4f09b62 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_active_images.m @@ -0,0 +1,14 @@ + +if ~exist('active_images'), + active_images = ones(1,n_ima); +end; +n_act = length(active_images); +if n_act < n_ima, + active_images = [active_images ones(1,n_ima-n_act)]; +else + if n_act > n_ima, + active_images = active_images(1:n_ima); + end; +end; + +ind_active = find(active_images); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_convergence.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_convergence.m new file mode 100755 index 0000000..8602c39 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_convergence.m @@ -0,0 +1,17 @@ +%%% Replay the set of solution vectors: + +N_iter = size(param_list,2); + +for nn = 1:N_iter, + + solution = param_list(:,nn); + + extract_parameters; + comp_error_calib; + + ext_calib; + + drawnow; + + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_planarity.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_planarity.m new file mode 100755 index 0000000..be0410b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/check_planarity.m @@ -0,0 +1,41 @@ +% Check the planarity of a structure: + +X = X_1; +N = size(X,2); + +%X(3,:) = 0.1*randn(1,N); + +om = rand(3,1); +T = 10*rand(3,1); +R = rodrigues(om); + +X = R * X + T*ones(1,N); + + + + + + +N = size(X,2); +X_mean = mean(X')'; + +Y = X - (X_mean*ones(1,N)); + +YY = Y*Y'; + +[U,S,V] = svd(YY); + +r = S(3,3)/S(2,2); + +% if r is less than 1e-4: + +R_transform = V'; +T_transform = -(V')*X_mean; + + +% Thresh for r: 1e-4 + +X_new = R_transform*X + T_transform*ones(1,N); + + +% If Xc = Rc * X_new + Tc, then Xc = Rc * R_transform * X + Tc + T_transform diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib.m new file mode 100755 index 0000000..047cc7b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib.m @@ -0,0 +1,99 @@ + +if ~exist('I_1'), + ima_read_calib; + if no_image_file, + disp('Cannot extract corners without images'); + return; + end; +end; + +check_active_images; + +%wintx = 10; % neigborhood of integration for +%winty = 10; % the corner finder + +fprintf(1,'\nExtraction of the grid corners on the images\n'); + +disp('Window size for corner finder (wintx and winty):'); +wintx = input('wintx ([] = 5) = '); +if isempty(wintx), wintx = 5; end; +wintx = round(wintx); +winty = input('winty ([] = 5) = '); +if isempty(winty), winty = 5; end; +winty = round(winty); + +fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + +if ~exist('map'), map = gray(256); end; + + +disp('WARNING!!! Do not forget to change dX_default and dY_default in click_calib.m!!!') + + +% Default size of the pattern squares; + +% Setup of JY (old at Caltech) +dX_default = 21.9250/11; +dY_default = 18.1250/9; + +% Setup of JY (new at Intel) +dX_default = 1.9750; +dY_default = 1.9865; + + +% Setup of Luis and Enrico +dX_default = 67.7/16; +dY_default = 50.65/12; + + +% Setup of German +dX_default = 10.16; +dY_default = 10.16; + +% Setup of JY (new at Intel) +dX_default = 1.9750*2.54; +dY_default = 1.9865*2.54; + +% Setup of JY - 3D calibration rig at Intel (new at Intel) +dX_default = 3; +dY_default = 3; + +% Useful option to add images: +kk_first = input('Start image number ([]=1=first): '); + +if isempty(kk_first), kk_first = 1; end; + +for kk = kk_first:n_ima, + if active_images(kk), + click_ima_calib; + else + eval(['dX_' num2str(kk) ' = NaN;']); + eval(['dY_' num2str(kk) ' = NaN;']); + + eval(['wintx_' num2str(kk) ' = NaN;']); + eval(['winty_' num2str(kk) ' = NaN;']); + + eval(['x_' num2str(kk) ' = NaN*ones(2,1);']); + eval(['X_' num2str(kk) ' = NaN*ones(3,1);']); + + eval(['n_sq_x_' num2str(kk) ' = NaN;']); + eval(['n_sq_y_' num2str(kk) ' = NaN;']); + end; +end; + + + +string_save = 'save calib_data active_images ind_active wintx winty n_ima type_numbering N_slots first_num image_numbers format_image calib_name Hcal Wcal nx ny map dX_default dY_default dX dY'; + +for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' n_sq_x_' num2str(kk) ' n_sq_y_' num2str(kk) ' wintx_' num2str(kk) ' winty_' num2str(kk) ' dX_' num2str(kk) ' dY_' num2str(kk)]; +end; + +eval(string_save); + +disp('done'); + +return; + +go_calib_optim; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib3D.m new file mode 100755 index 0000000..e761cd1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_calib3D.m @@ -0,0 +1,79 @@ + +if ~exist('I_1'), + ima_read_calib; + if no_image_file, + disp('Cannot extract corners without images'); + return; + end; +end; + +%wintx = 10; % neigborhood of integration for +%winty = 10; % the corner finder + +fprintf(1,'\nExtraction of the grid corners on the images\n'); + +disp('Window size for corner finder (wintx and winty):'); +wintx = input('wintx ([] = 5) = '); +if isempty(wintx), wintx = 5; end; +wintx = round(wintx); +winty = input('winty ([] = 5) = '); +if isempty(winty), winty = 5; end; +winty = round(winty); + + +fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + + +disp('WARNNG!!! Do not forget to change dX_default and dY_default in click_calib.m!!!') + + +% Default size of the pattern squares; + +% Setup of JY (old at Caltech) +dX_default = 21.9250/11; +dY_default = 18.1250/9; + +% Setup of JY (new at Intel) +dX_default = 1.9750; +dY_default = 1.9865; + + +% Setup of Luis and Enrico +dX_default = 67.7/16; +dY_default = 50.65/12; + + +% Setup of German +dX_default = 10.16; +dY_default = 10.16; + +% Setup of JY (new at Intel) +dX_default = 1.9750*2.54; +dY_default = 1.9865*2.54; + + +% Setup of JY - 3D calibration rig at Intel (new at Intel) +dX_default = 3; +dY_default = 3; + +% Useful option to add images: +kk_first = input('Start image number ([]=1=first): '); + +if isempty(kk_first), kk_first = 1; end; + +for kk = kk_first:n_ima, + click_ima_calib3D; %Simple version + %init_calib; %advanced vesion (more messy) +end; + + + +string_save = 'save calib_data wintx winty n_ima type_numbering N_slots first_num image_numbers format_image calib_name Hcal Wcal nx ny map dX_default dY_default dX dY'; + +for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' Hl_' num2str(kk) ' nl_sq_x_' num2str(kk) ' nl_sq_y_' num2str(kk) ' Hr_' num2str(kk) ' nr_sq_x_' num2str(kk) ' nr_sq_y_' num2str(kk)]; +end; + +eval(string_save); + +go_calib_optim3D; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib.m new file mode 100755 index 0000000..5197870 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib.m @@ -0,0 +1,218 @@ + % Cleaned-up version of init_calib.m + + fprintf(1,'\nProcessing image %d...\n',kk); + + eval(['I = I_' num2str(kk) ';']); + + figure(2); + image(I); + colormap(map); + + title(['Click on the four extreme corners of the rectangular pattern... Image ' num2str(kk)]); + + disp('Click on the four extreme corners of the rectangular complete pattern...'); + + [x,y] = ginput3(4); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + if (x(2) > x(1)), + x4 = x(1);y4 = y(1); x3 = x(2); y3 = y(2); + else + x4 = x(2);y4 = y(2); x3 = x(1); y3 = y(1); + end; + if (x(3) > x(4)), + x2 = x(3);y2 = y(3); x1 = x(4); y1 = y(4); + else + x2 = x(4);y2 = y(4); x1 = x(3); y1 = y(3); + end; + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'g-'); + plot(x,y,'og'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','g','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','g','Fontsize',14); + hold off; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=' num2str(dX_default) 'cm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=' num2str(dY_default) 'cm) = ']); + if isempty(dX), dX = dX_default; else dX_default = dX; end; + if isempty(dY), dY = dY_default; else dY_default = dY; end; + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planar collineation: (return the normalization matrix as well) + + [Homo,Hnorm,inv_Hnorm] = compute_homography ([a00 a10 a11 a01],[0 1 1 0;0 0 1 1;1 1 1 1]); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + + + + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + + + %save all_corners x y grid_pts + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + + % Global Homography from plane to pixel coordinates: + + + + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + image(I); colormap(map); hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + + + % Saves all the data into variables: + + eval(['dX_' num2str(kk) ' = dX;']); + eval(['dY_' num2str(kk) ' = dY;']); + + eval(['wintx_' num2str(kk) ' = wintx;']); + eval(['winty_' num2str(kk) ' = winty;']); + + eval(['x_' num2str(kk) ' = x;']); + eval(['X_' num2str(kk) ' = X;']); + + eval(['n_sq_x_' num2str(kk) ' = n_sq_x;']); + eval(['n_sq_y_' num2str(kk) ' = n_sq_y;']); + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib3D.m new file mode 100755 index 0000000..7718268 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/click_ima_calib3D.m @@ -0,0 +1,482 @@ + % Cleaned-up version of init_calib.m + + eval(['I = I_' num2str(kk) ';']); + + figure(2); + image(I); + colormap(map); + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%% LEFT PATTERN ACQUISITION %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + title(['Click on the four extreme corners of the left rectangular pattern... Image ' num2str(kk)]); + + disp('Click on the four extreme corners of the left rectangular pattern...'); + + [x,y] = ginput3(4); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + if (x(2) > x(1)), + x4 = x(1);y4 = y(1); x3 = x(2); y3 = y(2); + else + x4 = x(2);y4 = y(2); x3 = x(1); y3 = y(1); + end; + if (x(3) > x(4)), + x2 = x(3);y2 = y(3); x1 = x(4); y1 = y(4); + else + x2 = x(4);y2 = y(4); x1 = x(3); y1 = y(3); + end; + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'g-'); + plot(x,y,'og'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','g','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','g','Fontsize',14); + hold off; + + drawnow; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + + if 1, + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=' num2str(dX_default) 'cm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=' num2str(dY_default) 'cm) = ']); + if isempty(dX), dX = dX_default; else dX_default = dX; end; + if isempty(dY), dY = dY_default; else dY_default = dY; end; + + else + + dX = 3; + dY = 3; + + end; + + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planart collineation: (return the normalization matrice as well) + + [Homo,Hnorm,inv_Hnorm] = compute_collineation (a00, a10, a11, a01); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + if 1, + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + end; + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + %save all_corners x y grid_pts + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + + % Global Homography from plane to pixel coordinates: + + H_total = [1 0 -1 ; 0 1 -1 ; 0 0 1]*Homo*[1 0 0;0 -1 1;0 0 1]*[1/W 0 0 ; 0 1/L 0; 0 0 1]; + % WARNING!!! the first matrix (on the left side) takes care of the transformation of the pixel cooredinates by -1 (previous line) + % If it is not done, then this matrix should not appear (in C) + H_total = H_total / H_total(3,3); + + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + image(I); colormap(map); hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + + + % The left pannel info: + + xl = x; + Xl = X; + nl_sq_x = n_sq_x; + nl_sq_y = n_sq_y; + Hl = H_total; + + + + + + + %%%%%%%%%%%%%%%%%%%%%%%%% RIGHT PATTERN ACQUISITION %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + x1 = a10(1)/a10(3); + x4 = a11(1)/a11(3); + + y1 = a10(2)/a10(3); + y4 = a11(2)/a11(3); + + + figure(2); + hold on; + plot([x1 x4],[y1 y4],'c-'); + plot([x1 x4],[y1 y4],'co'); + hold off; + + title(['Click on the two remaining extreme corners of the right rectangular pattern... Image ' num2str(kk)]); + + disp('Click on the two remaining extreme corners of the right rectangular pattern...'); + + [x,y] = ginput3(2); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + x2 = x(2); + x3 = x(1); + + y2 = y(2); + y3 = y(1); + + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'c-'); + plot(x,y,'oc'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','c','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','c','Fontsize',14); + hold off; + drawnow; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + + if 1, + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=' num2str(dX_default) 'cm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=' num2str(dY_default) 'cm) = ']); + if isempty(dX), dX = dX_default; else dX_default = dX; end; + if isempty(dY), dY = dY_default; else dY_default = dY; end; + + else + + dX = 3; + dY = 3; + + end; + + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planart collineation: (return the normalization matrice as well) + + [Homo,Hnorm,inv_Hnorm] = compute_collineation (a00, a10, a11, a01); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + if 1, + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + end; + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + %save all_corners x y grid_pts + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + + % Global Homography from plane to pixel coordinates: + + H_total = [1 0 -1 ; 0 1 -1 ; 0 0 1]*Homo*[1 0 0;0 -1 1;0 0 1]*[1/W 0 0 ; 0 1/L 0; 0 0 1]; + % WARNING!!! the first matrix (on the left side) takes care of the transformation of the pixel cooredinates by -1 (previous line) + % If it is not done, then this matrix should not appear (in C) + H_total = H_total / H_total(3,3); + + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + + + % The right pannel info: + + xr = x; + Xr = X; + nr_sq_x = n_sq_x; + nr_sq_y = n_sq_y; + Hr = H_total; + + + +%%%%%%%% REGROUP THE LEFT AND RIHT PATTERNS %%%%%%%%%%%%% + + +Xr2 = [0 0 1;0 1 0;-1 0 0]*Xr + [dX*nl_sq_x;0;0]*ones(1,length(Xr)); + + +x = [xl xr]; + +X = [Xl Xr2]; + + + + eval(['x_' num2str(kk) ' = x;']); + eval(['X_' num2str(kk) ' = X;']); + + eval(['nl_sq_x_' num2str(kk) ' = nl_sq_x;']); + eval(['nl_sq_y_' num2str(kk) ' = nl_sq_y;']); + + eval(['nr_sq_x_' num2str(kk) ' = nr_sq_x;']); + eval(['nr_sq_y_' num2str(kk) ' = nr_sq_y;']); + + % Save the global planar homography: + + eval(['Hl_' num2str(kk) ' = Hl;']); + eval(['Hr_' num2str(kk) ' = Hr;']); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion.m new file mode 100755 index 0000000..a0f03de --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion.m @@ -0,0 +1,38 @@ +function [x_comp] = comp_distortion(x_dist,k2); + +% [x_comp] = comp_distortion(x_dist,k2); +% +% compensates the radial distortion of the camera +% on the image plane. +% +% x_dist : the image points got without considering the +% radial distortion. +% x : The image plane points after correction for the distortion +% +% x and x_dist are 2xN arrays +% +% NOTE : This compensation has to be done after the substraction +% of the center of projection, and division by the focal +% length. +% +% (do it up to a second order approximation) + +[two,N] = size(x_dist); + +if (two ~= 2 ), + error('ERROR : The dimension of the points should be 2xN'); +end; + +if length(k2) > 2, + [x_comp] = comp_distortion_oulu(x_dist,k2); +else + +radius_2= x_dist(1,:).^2 + x_dist(2,:).^2; +radial_distortion = 1 + ones(2,1)*(k2 * radius_2); +radius_2_comp = (x_dist(1,:).^2 + x_dist(2,:).^2) ./ radial_distortion(1,:); +radial_distortion = 1 + ones(2,1)*(k2 * radius_2_comp); +x_comp = x_dist ./ radial_distortion; + +end; + +%% Function completely checked : It works fine !!! \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion2.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion2.m new file mode 100755 index 0000000..532ee9a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion2.m @@ -0,0 +1,71 @@ +function [x_comp] = comp_distortion(x_dist,k2); + +% [x_comp] = comp_distortion(x_dist,k2); +% +% compensates the radial distortion of the camera +% on the image plane. +% +% x_dist : the image points got without considering the +% radial distortion. +% k2: Radial distortion factor +% +% x : The image plane points after correction for the distortion +% +% x and x_dist are 2xN arrays +% +% NOTE : This compensation has to be done after the substraction +% of the center of projection, and division by the focal +% length. +% +% Solve for cubic roots using method from Numerical Recipes in C 2nd Ed. +% pages 184-185. + + +% California Institute of Technology +% (c) Jean-Yves Bouguet - April 27th, 1998 + +% fully checked! JYB, april 27th, 1998 - 2am + +if k2 ~= 0, + +[two,N] = size(x_dist); + +if (two ~= 2 ), + error('ERROR : The dimension of the points should be 2xN'); +end; + + +ph = atan2(x_dist(2,:),x_dist(1,:)); + +Q = -1/(3*k2); +R = -x_dist(1,:)./(2*k2*cos(ph)); + +R2 = R.^2; +Q3 = Q^3; + + +if k2 < 0, + + % this works in all practical situations (it starts failing for very large + % values of k2) + + th = acos(R./sqrt(Q3)); + r = -2*sqrt(Q)*cos((th-2*pi)/3); + +else + + % note: this always works, even for ridiculous values of k2 + + A = (sqrt(R2-Q3)-R).^(1/3); + B = Q*(1./A); + r = (A+B); + +end; + +x_comp = [r.*cos(ph); r.*sin(ph)]; + +else + + x_comp = x_dist; + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion_oulu.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion_oulu.m new file mode 100755 index 0000000..67d02d5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_distortion_oulu.m @@ -0,0 +1,47 @@ +function [x] = comp_distortion_oulu(xd,k); + +%comp_distortion_oulu.m +% +%[x] = comp_distortion_oulu(xd,k) +% +%Compensates for radial and tangential distortion. Model From Oulu university. +%For more informatino about the distortion model, check the forward projection mapping function: +%project_points.m +% +%INPUT: xd: distorted (normalized) point coordinates in the image plane (2xN matrix) +% k: Distortion coefficients (radial and tangential) (4x1 vector) +% +%OUTPUT: x: undistorted (normalized) point coordinates in the image plane (2xN matrix) +% +%Method: Iterative method for compensation. +% +%NOTE: This compensation has to be done after the subtraction +% of the principal point, and division by the focal length. + + +if length(k) < 4, + + [x] = comp_distortion(xd,k); + +else + + + k1 = k(1); + k2 = k(2); + p1 = k(3); + p2 = k(4); + + x = xd; % initial guess + + for kk=1:5; + + r_2 = sum(x.^2); + k_radial = 1 + k1 * r_2 + k2 * r_2.^2; + delta_x = [2*p1*x(1,:).*x(2,:) + p2*(r_2 + 2*x(1,:).^2) ; + p1 * (r_2 + 2*x(2,:).^2)+2*p2*x(1,:).*x(2,:)]; + x = (xd - delta_x)./(ones(2,1)*k_radial); + + end; + +end; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_error_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_error_calib.m new file mode 100755 index 0000000..f8d6fde --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/comp_error_calib.m @@ -0,0 +1,40 @@ +%%%%%%%%%%%%%%%%%%%% RECOMPUTES THE REPROJECTION ERROR %%%%%%%%%%%%%%%%%%%%%%%% + +check_active_images; + +% Reproject the patterns on the images, and compute the pixel errors: + +ex = []; % Global error vector +x = []; % Detected corners on the image plane +y = []; % Reprojected points + +for kk = 1:n_ima, + + eval(['omckk = omc_' num2str(kk) ';']); + eval(['Tckk = Tc_' num2str(kk) ';']); + + if active_images(kk) & (~isnan(omckk(1,1))), + + Rkk = rodrigues(omckk); + + eval(['y_' num2str(kk) ' = project2_oulu(X_' num2str(kk) ',Rkk,Tckk,fc,cc,kc);']); + + eval(['ex_' num2str(kk) ' = x_' num2str(kk) ' -y_' num2str(kk) ';']); + + eval(['x_kk = x_' num2str(kk) ';']); + + eval(['ex = [ex ex_' num2str(kk) '];']); + eval(['x = [x x_' num2str(kk) '];']); + eval(['y = [y y_' num2str(kk) '];']); + + else + + eval(['y_' num2str(kk) ' = NaN*ones(2,1);']); + + eval(['ex_' num2str(kk) ' = NaN*ones(2,1);']); + + end; + +end; + +err_std = std(ex')'; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_collineation.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_collineation.m new file mode 100755 index 0000000..809c309 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_collineation.m @@ -0,0 +1,66 @@ +function [H,Hnorm,inv_Hnorm] = compute_collineation (a00, a10, a11, a01); + +% new formalism using homographies + +a00 = a00 / a00(3); +a10 = a10 / a10(3); +a11 = a11 / a11(3); +a01 = a01 / a01(3); + + +% Prenormalization of point coordinates (very important): +% (Affine normalization) + +ax = [a00(1);a10(1);a11(1);a01(1)]; +ay = [a00(2);a10(2);a11(2);a01(2)]; + +mxx = mean(ax); +myy = mean(ay); +ax = ax - mxx; +ay = ay - myy; + +scxx = mean(abs(ax)); +scyy = mean(abs(ay)); + + +Hnorm = [1/scxx 0 -mxx/scxx;0 1/scyy -myy/scyy;0 0 1]; +inv_Hnorm = [scxx 0 mxx ; 0 scyy myy; 0 0 1]; + + +a00n = Hnorm*a00; +a10n = Hnorm*a10; +a11n = Hnorm*a11; +a01n = Hnorm*a01; + + +% Computation of the vanishing points: + +V1n = cross(cross(a00n,a10n),cross(a01n,a11n)); +V2n = cross(cross(a00n,a01n),cross(a10n,a11n)); + +V1 = inv_Hnorm*V1n; +V2 = inv_Hnorm*V2n; + + +% Normalizaion of the vanishing points: + +V1n = V1n/norm(V1n); +V2n = V2n/norm(V2n); + + +% Closed-form solution of the coefficients: + +alpha_x = (a10n(2)*a00n(1) - a10n(1)*a00n(2))/(V1n(2)*a10n(1)-V1n(1)*a10n(2)); + +alpha_y = (a01n(2)*a00n(1) - a01n(1)*a00n(2))/(V2n(2)*a01n(1)-V2n(1)*a01n(2)); + + +% Remaining Homography + +Hrem = [alpha_x*V1n alpha_y*V2n a00n]; + + +% Final homography: + +H = inv_Hnorm*Hrem; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic.m new file mode 100755 index 0000000..4b4d7dd --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic.m @@ -0,0 +1,123 @@ +function [omckk,Tckk,Rckk,H,x,ex,JJ] = compute_extrinsic(x_kk,X_kk,fc,cc,kc,MaxIter,thresh_cond), + +%compute_extrinsic +% +%[omckk,Tckk,Rckk,H,x,ex] = compute_extrinsic(x_kk,X_kk,fc,cc,kc,refine) +% +%Computes the extrinsic parameters attached to a 3D structure X_kk given its projection +%on the image plane x_kk and the intrinsic camera parameters fc, cc and kc. +%Works with planar and non-planar structures. +% +%INPUT: x_kk: Feature locations on the images +% X_kk: Corresponding grid coordinates +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% refine: set to 1 for refining the extrinsic parameters iteratively +% [OPTIONAL: Default value: 1] +% +%OUTPUT: omckk: 3D rotation vector attached to the grid positions in space +% Tckk: 3D translation vector attached to the grid positions in space +% Rckk: 3D rotation matrices corresponding to the omc vectors +% H: Homography between points on the grid and points on the image plane (in pixel) +% This makes sense only if the planar that is used in planar. +% x: Reprojections of the points on the image plane +% ex: Reprojection error: ex = x_kk - x; +% +%Method: Computes the normalized point coordinates, then computes the 3D pose +% +%Important functions called within that program: +% +%normalize: Computes the normalize image point coordinates. +% +%pose3D: Computes the 3D pose of the structure given the normalized image projection. +% +%project_points.m: Computes the 2D image projections of a set of 3D points + + + +if nargin < 7, + thresh_cond = inf; +end; + + +if nargin < 6, + MaxIter = 20; +end; + + + +if nargin < 5, + kc = zeros(4,1); + if nargin < 4, + cc = zeros(2,1); + if nargin < 3, + fc = ones(2,1); + if nargin < 2, + error('Need 2D projections and 3D points (in compute_extrinsic.m)'); + return; + end; + end; + end; +end; + + +% Initialization: + +[omckk,Tckk,Rckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc); + +% Refinement: + +[omckk,Tckk,Rckk,JJ] = compute_extrinsic_refine(omckk,Tckk,x_kk,X_kk,fc,cc,kc,MaxIter,thresh_cond); + + +% computation of the homography (not useful in the end) + +H = [Rckk(:,1:2) Tckk]; + +% Computes the reprojection error in pixels: + +x = project_points(X_kk,omckk,Tckk,fc,cc,kc); + +ex = x_kk - x; + + +% Converts the homography in pixel units: + +KK = [fc(1) 0 cc(1);0 fc(2) cc(2); 0 0 1]; + +H = KK*H; + + + + +return; + + +% Test of compte extrinsic: + +Np = 4; +sx = 10; +sy = 10; +sz = 5; + +om = randn(3,1); +T = [0;0;100]; + +noise = 2/1000; + +XX = [sx*randn(1,Np);sy*randn(1,Np);sz*randn(1,Np)]; +xx = project_points(XX,om,T); + +xxn = xx + noise * randn(2,Np); + +[omckk,Tckk] = compute_extrinsic(xxn,XX); + +[om omckk om-omckk] +[T Tckk T-Tckk] + +figure(3); +plot(xx(1,:),xx(2,:),'r+'); +hold on; +plot(xxn(1,:),xxn(2,:),'g+'); +hold off; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_init.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_init.m new file mode 100755 index 0000000..207ea30 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_init.m @@ -0,0 +1,149 @@ +function [omckk,Tckk,Rckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc), + +%compute_extrinsic +% +%[omckk,Tckk,Rckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc) +% +%Computes the extrinsic parameters attached to a 3D structure X_kk given its projection +%on the image plane x_kk and the intrinsic camera parameters fc, cc and kc. +%Works with planar and non-planar structures. +% +%INPUT: x_kk: Feature locations on the images +% X_kk: Corresponding grid coordinates +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% +%OUTPUT: omckk: 3D rotation vector attached to the grid positions in space +% Tckk: 3D translation vector attached to the grid positions in space +% Rckk: 3D rotation matrices corresponding to the omc vectors +% +%Method: Computes the normalized point coordinates, then computes the 3D pose +% +%Important functions called within that program: +% +%normalize: Computes the normalize image point coordinates. +% +%pose3D: Computes the 3D pose of the structure given the normalized image projection. +% +%project_points.m: Computes the 2D image projections of a set of 3D points + + + + +if nargin < 5, + kc = zeros(4,1); + if nargin < 4, + cc = zeros(2,1); + if nargin < 3, + fc = ones(2,1); + if nargin < 2, + error('Need 2D projections and 3D points (in compute_extrinsic.m)'); + return; + end; + end; + end; +end; + + + +% Compute the normalized coordinates: + +xn = normalize(x_kk,fc,cc,kc); + + + +Np = size(xn,2); + +%% Check for planarity of the structure: + +X_mean = mean(X_kk')'; + +Y = X_kk - (X_mean*ones(1,Np)); + +YY = Y*Y'; + +[U,S,V] = svd(YY); + +r = S(3,3)/S(2,2); + +if (r < 1e-3)|(Np < 6), %1e-3, %1e-4, %norm(X_kk(3,:)) < eps, % Test of planarity + + %fprintf(1,'Planar structure detected: r=%f\n',r); + + % Transform the plane to bring it in the Z=0 plane: + + R_transform = V'; + + if det(R_transform) < 0, R_transform = -R_transform; end; + + T_transform = -(R_transform)*X_mean; + + X_new = R_transform*X_kk + T_transform*ones(1,Np); + + + % Compute the planar homography: + + H = compute_homography (xn,X_new(1:2,:)); + + % De-embed the motion parameters from the homography: + + sc = mean([norm(H(:,1));norm(H(:,2))]); + + H = H/sc; + + omckk = rodrigues([H(:,1:2) cross(H(:,1),H(:,2))]); + Rckk = rodrigues(omckk); + Tckk = H(:,3); + + %If Xc = Rckk * X_new + Tckk, then Xc = Rckk * R_transform * X_kk + Tckk + T_transform + + Tckk = Tckk + Rckk* T_transform; + Rckk = Rckk * R_transform; + + omckk = rodrigues(Rckk); + Rckk = rodrigues(omckk); + + +else + + %fprintf(1,'Non planar structure detected: r=%f\n',r); + + % Computes an initial guess for extrinsic parameters (works for general 3d structure, not planar!!!): + % The DLT method is applied here!! + + J = zeros(2*Np,12); + + xX = (ones(3,1)*xn(1,:)).*X_kk; + yX = (ones(3,1)*xn(2,:)).*X_kk; + + J(1:2:end,[1 4 7]) = -X_kk'; + J(2:2:end,[2 5 8]) = X_kk'; + J(1:2:end,[3 6 9]) = xX'; + J(2:2:end,[3 6 9]) = -yX'; + J(1:2:end,12) = xn(1,:)'; + J(2:2:end,12) = -xn(2,:)'; + J(1:2:end,10) = -ones(Np,1); + J(2:2:end,11) = ones(Np,1); + + JJ = J'*J; + [U,S,V] = svd(JJ); + + RR = reshape(V(1:9,12),3,3); + + if det(RR) < 0, + V(:,12) = -V(:,12); + RR = -RR; + end; + + [Ur,Sr,Vr] = svd(RR); + + Rckk = Ur*Vr'; + + sc = norm(V(1:9,12)) / norm(Rckk(:)); + Tckk = V(10:12,12)/sc; + + omckk = rodrigues(Rckk); + Rckk = rodrigues(omckk); + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_refine.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_refine.m new file mode 100755 index 0000000..69474c4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_extrinsic_refine.m @@ -0,0 +1,110 @@ +function [omckk,Tckk,Rckk,JJ] = compute_extrinsic_refine(omc_init,Tc_init,x_kk,X_kk,fc,cc,kc,MaxIter,thresh_cond), + +%compute_extrinsic +% +%[omckk,Tckk,Rckk] = compute_extrinsic_refine(x_kk,X_kk,fc,cc,kc,MaxIter) +% +%Computes the extrinsic parameters attached to a 3D structure X_kk given its projection +%on the image plane x_kk and the intrinsic camera parameters fc, cc and kc. +%Works with planar and non-planar structures. +% +%INPUT: x_kk: Feature locations on the images +% X_kk: Corresponding grid coordinates +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% MaxIter: Maximum number of iterations +% +%OUTPUT: omckk: 3D rotation vector attached to the grid positions in space +% Tckk: 3D translation vector attached to the grid positions in space +% Rckk: 3D rotation matrices corresponding to the omc vectors + +% +%Method: Computes the normalized point coordinates, then computes the 3D pose +% +%Important functions called within that program: +% +%normalize: Computes the normalize image point coordinates. +% +%pose3D: Computes the 3D pose of the structure given the normalized image projection. +% +%project_points.m: Computes the 2D image projections of a set of 3D points + + +if nargin < 9, + thresh_cond = inf; +end; + + +if nargin < 8, + MaxIter = 20; +end; + + +if nargin < 7, + kc = zeros(4,1); + if nargin < 6, + cc = zeros(2,1); + if nargin < 5, + fc = ones(2,1); + if nargin < 4, + error('Need 2D projections and 3D points (in compute_extrinsic_refine.m)'); + return; + end; + end; + end; +end; + + +% Initialization: + +omckk = omc_init; +Tckk = Tc_init; + + +% Final optimization (minimize the reprojection error in pixel): +% through Gradient Descent: + +param = [omckk;Tckk]; + +change = 1; + +iter = 0; + +%keyboard; + +%fprintf(1,'Gradient descent iterations: '); + +while (change > 1e-10)&(iter < MaxIter), + + %fprintf(1,'%d...',iter+1); + + [x,dxdom,dxdT] = project_points(X_kk,omckk,Tckk,fc,cc,kc); + + ex = x_kk - x; + + %keyboard; + + JJ = [dxdom dxdT]; + + if cond(JJ) > thresh_cond, + change = 0; + else + + JJ2 = JJ'*JJ; + + param_innov = inv(JJ2)*(JJ')*ex(:); + param_up = param + param_innov; + change = norm(param_innov)/norm(param_up); + param = param_up; + iter = iter + 1; + + omckk = param(1:3); + Tckk = param(4:6); + end; + +end; + +%fprintf(1,'\n'); + +Rckk = rodrigues(omckk); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_homography.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_homography.m new file mode 100755 index 0000000..fcc9003 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/compute_homography.m @@ -0,0 +1,163 @@ +function [H,Hnorm,inv_Hnorm] = compute_homography (m,M); + +%compute_homography +% +%[H,Hnorm,inv_Hnorm] = compute_homography (m,M) +% +%Computes the planar homography between the point coordinates on the plane (M) and the image +%point coordinates (m). +% +%INPUT: m: homogeneous coordinates in the image plane (3xN matrix) +% M: homogeneous coordinates in the plane in 3D (3xN matrix) +% +%OUTPUT: H: Homography matrix (3x3 homogeneous matrix) +% Hnorm: Normlization matrix used on the points before homography computation +% (useful for numerical stability is points in pixel coordinates) +% inv_Hnorm: The inverse of Hnorm +% +%Definition: m ~ H*M where "~" means equal up to a non zero scalar factor. +% +%Method: First computes an initial guess for the homography through quasi-linear method. +% Then, if the total number of points is larger than 4, optimize the solution by minimizing +% the reprojection error (in the least squares sense). +% +% +%Important functions called within that program: +% +%comp_distortion_oulu: Undistorts pixel coordinates. +% +%compute_homography.m: Computes the planar homography between points on the grid in 3D, and the image plane. +% +%project_points.m: Computes the 2D image projections of a set of 3D points, and also returns te Jacobian +% matrix (derivative with respect to the intrinsic and extrinsic parameters). +% This function is called within the minimization loop. + + + + +Np = size(m,2); + +if size(m,1)<3, + m = [m;ones(1,Np)]; +end; + +if size(M,1)<3, + M = [M;ones(1,Np)]; +end; + + +m = m ./ (ones(3,1)*m(3,:)); +M = M ./ (ones(3,1)*M(3,:)); + +% Prenormalization of point coordinates (very important): +% (Affine normalization) + +ax = m(1,:); +ay = m(2,:); + +mxx = mean(ax); +myy = mean(ay); +ax = ax - mxx; +ay = ay - myy; + +scxx = mean(abs(ax)); +scyy = mean(abs(ay)); + + +Hnorm = [1/scxx 0 -mxx/scxx;0 1/scyy -myy/scyy;0 0 1]; +inv_Hnorm = [scxx 0 mxx ; 0 scyy myy; 0 0 1]; + +mn = Hnorm*m; + +% Compute the homography between m and mn: + +% Build the matrix: + +L = zeros(2*Np,9); + +L(1:2:2*Np,1:3) = M'; +L(2:2:2*Np,4:6) = M'; +L(1:2:2*Np,7:9) = -((ones(3,1)*mn(1,:)).* M)'; +L(2:2:2*Np,7:9) = -((ones(3,1)*mn(2,:)).* M)'; + +if Np > 4, + L = L'*L; +end; + +[U,S,V] = svd(L); + +hh = V(:,9); +hh = hh/hh(9); + +Hrem = reshape(hh,3,3)'; +%Hrem = Hrem / Hrem(3,3); + +% Final homography: + +H = inv_Hnorm*Hrem; + + +%%% Homography refinement if there are more than 4 points: + +if Np > 4, + + % Final refinement: + + hhv = reshape(H',9,1); + hhv = hhv(1:8); + + for iter=1:10, + + mrep = H * M; + + J = zeros(2*Np,8); + + MMM = (M ./ (ones(3,1)*mrep(3,:))); + + J(1:2:2*Np,1:3) = -MMM'; + J(2:2:2*Np,4:6) = -MMM'; + + mrep = mrep ./ (ones(3,1)*mrep(3,:)); + + m_err = m(1:2,:) - mrep(1:2,:); + m_err = m_err(:); + + MMM2 = (ones(3,1)*mrep(1,:)) .* MMM; + MMM3 = (ones(3,1)*mrep(2,:)) .* MMM; + + J(1:2:2*Np,7:8) = MMM2(1:2,:)'; + J(2:2:2*Np,7:8) = MMM3(1:2,:)'; + + MMM = (M ./ (ones(3,1)*mrep(3,:)))'; + + hh_innov = inv(J'*J)*J'*m_err; + + hhv_up = hhv - hh_innov; + + H_up = reshape([hhv_up;1],3,3)'; + + %norm(m_err) + %norm(hh_innov) + + hhv = hhv_up; + H = H_up; + + end; + +end; + + + + + +return; + +%test of Jacobian + +mrep = H*M; +mrep = mrep ./ (ones(3,1)*mrep(3,:)); + +m_err = mrep(1:2,:) - m(1:2,:); +figure(8); +plot(m_err(1,:),m_err(2,:),'r+'); +std(m_err') diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/convert_oulu.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/convert_oulu.m new file mode 100755 index 0000000..726806e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/convert_oulu.m @@ -0,0 +1,35 @@ +%% Converts data file from oulu to mine: + +load cademo, +n_ima = 0; + +no_error = 1; + +ii = 1; + +while no_error, + + dataname = ['data' num2str(ii)]; + + if exist(dataname), + + n_ima = n_ima +1; + + eval(['x_' num2str(ii) '= ' dataname '(:,4:5)'';']) + eval(['X_' num2str(ii) '= ' dataname '(:,1:3)'';']) + + else + no_error = 0; + end; + + ii = ii + 1; + +end; + +nx = 500; +ny = 500; + +no_image = 1; +no_grid = 1; + +save data n_ima x_1 X_1 x_2 X_2 x_3 X_3 nx ny no_image no_grid diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/cornerfinder.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/cornerfinder.m new file mode 100755 index 0000000..9bfa51f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/cornerfinder.m @@ -0,0 +1,215 @@ +function [xc,good,bad,type] = cornerfinder(xt,I,wintx,winty,wx2,wy2); + +%[xc] = cornerfinder(xt,I); +% +%Finds the sub-pixel corners on the image I with initial guess xt +%xt and xc are 2xN matrices. The first component is the x coordinate +%(horizontal) and the second component is the y coordinate (vertical) +% +%Based on Harris corner finder method +% +%Finds corners to a precision below .1 pixel! +%Oct. 14th, 1997 - UPDATED to work with vertical and horizontal edges as well!!! +%Sept 1998 - UPDATED to handle diverged points: we keep the original points +%good is a binary vector indicating wether a feature point has been properly +%found. +% +%Add a zero zone of size wx2,wy2 +%July 15th, 1999 - Bug on the mask building... fixed + change to Gaussian mask with higher +%resolution and larger number of iterations. + + +% California Institute of Technology +% (c) Jean-Yves Bouguet -- Oct. 14th, 1997 + + + +line_feat = 1; % set to 1 to allow for extraction of line features. + +xt = xt'; +xt = fliplr(xt); + + +if nargin < 4, + winty = 5; + if nargin < 3, + wintx = 5; + end; +end; + + +if nargin < 6, + wx2 = -1; + wy2 = -1; +end; + + +%mask = ones(2*wintx+1,2*winty+1); +mask = exp(-((-wintx:wintx)'/(wintx)).^2) * exp(-((-winty:winty)/(winty)).^2); + + +if (wx2>0) & (wy2>0), + if ((wintx - wx2)>=2)&((winty - wy2)>=2), + mask(wintx+1-wx2:wintx+1+wx2,winty+1-wy2:winty+1+wy2)= zeros(2*wx2+1,2*wy2+1); + end; +end; + +offx = [-wintx:wintx]'*ones(1,2*winty+1); +offy = ones(2*wintx+1,1)*[-winty:winty]; + +resolution = 0.005; + +MaxIter = 10; + +[nx,ny] = size(I); +N = size(xt,1); + +xc = xt; % first guess... they don't move !!! + +type = zeros(1,N); + + +for i=1:N, + + v_extra = resolution + 1; % just larger than resolution + + compt = 0; % no iteration yet + + while (norm(v_extra) > resolution) & (compt 0, % the sub pixel + vIx = [itIx 1-itIx 0]'; % accuracy. + else + vIx = [0 1+itIx -itIx]'; + end; + if itIy > 0, + vIy = [itIy 1-itIy 0]; + else + vIy = [0 1+itIy -itIy]; + end; + + + % What if the sub image is not in? + + if (crIx-wintx-2 < 1), xmin=1; xmax = 2*wintx+5; + elseif (crIx+wintx+2 > nx), xmax = nx; xmin = nx-2*wintx-4; + else + xmin = crIx-wintx-2; xmax = crIx+wintx+2; + end; + + if (crIy-winty-2 < 1), ymin=1; ymax = 2*winty+5; + elseif (crIy+winty+2 > ny), ymax = ny; ymin = ny-2*winty-4; + else + ymin = crIy-winty-2; ymax = crIy+winty+2; + end; + + + SI = I(xmin:xmax,ymin:ymax); % The necessary neighborhood + SI = conv2(conv2(SI,vIx,'same'),vIy,'same'); + SI = SI(2:2*wintx+4,2:2*winty+4); % The subpixel interpolated neighborhood + [gy,gx] = gradient(SI); % The gradient image + gx = gx(2:2*wintx+2,2:2*winty+2); % extraction of the useful parts only + gy = gy(2:2*wintx+2,2:2*winty+2); % of the gradients + + px = cIx + offx; + py = cIy + offy; + + gxx = gx .* gx .* mask; + gyy = gy .* gy .* mask; + gxy = gx .* gy .* mask; + + + bb = [sum(sum(gxx .* px + gxy .* py)); sum(sum(gxy .* px + gyy .* py))]; + + a = sum(sum(gxx)); + b = sum(sum(gxy)); + c = sum(sum(gyy)); + + dt = a*c - b^2; + + xc2 = [c*bb(1)-b*bb(2) a*bb(2)-b*bb(1)]/dt; + + + %keyboard; + + if line_feat, + + G = [a b;b c]; + [U,S,V] = svd(G); + + %keyboard; + + % If non-invertible, then project the point onto the edge orthogonal: + + if (S(1,1)/S(2,2) > 50), + % projection operation: + xc2 = xc2 + sum((xc(i,:)-xc2).*(V(:,2)'))*V(:,2)'; + type(i) = 1; + end; + + end; + + + %keyboard; + +% G = [a b;b c]; +% [U,S,V] = svd(G); + + +% if S(1,1)/S(2,2) > 150, +% bb2 = U'*bb; +% xc2 = (V*[bb2(1)/S(1,1) ;0])'; +% else +% xc2 = [c*bb(1)-b*bb(2) a*bb(2)-b*bb(1)]/dt; +% end; + + + %if (abs(a)> 50*abs(c)), +% xc2 = [(c*bb(1)-b*bb(2))/dt xc(i,2)]; +% elseif (abs(c)> 50*abs(a)) +% xc2 = [xc(i,1) (a*bb(2)-b*bb(1))/dt]; +% else +% xc2 = [c*bb(1)-b*bb(2) a*bb(2)-b*bb(1)]/dt; +% end; + + %keyboard; + + v_extra = xc(i,:) - xc2; + + xc(i,:) = xc2; + +% keyboard; + + compt = compt + 1; + + end +end; + + +% check for points that diverge: + +delta_x = xc(:,1) - xt(:,1); +delta_y = xc(:,2) - xt(:,2); + +%keyboard; + + +bad = (abs(delta_x) > wintx) | (abs(delta_y) > winty); +good = ~bad; +in_bad = find(bad); + +% For the diverged points, keep the original guesses: + +xc(in_bad,:) = xt(in_bad,:); + +xc = fliplr(xc); +xc = xc'; + +bad = bad'; +good = good'; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/count_squares.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/count_squares.m new file mode 100755 index 0000000..0e226c0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/count_squares.m @@ -0,0 +1,74 @@ +function ns = count_squares(I,x1,y1,x2,y2,win); + +%keyboard; + +[ny,nx] = size(I); + +lambda = [y1 - y2;x2 - x1;x1*y2 - x2*y1]; + +lambda = 1/sqrt(lambda(1)^2 + lambda(2)^2) * lambda; + +l1 = lambda + [0;0;win]; +l2 = lambda - [0;0;win]; + + +dx = x2-x1; +dy = y2 - y1; + + +if abs(dx) > abs(dy), + + if x2 > x1, + xs = x1:x2; + else + xs = x1:-1:x2; + end; + + ys = -(lambda(3) + lambda(1)*xs)/lambda(2); + +else + + if y2 > y1, + ys = y1:y2; + else + ys = y1:-1:y2; + end; + xs = -(lambda(3) + lambda(2)*ys)/lambda(1); + +end; + + + + Np = length(xs); + + xs_mat = ones(2*win + 1,1)*xs; + ys_mat = ones(2*win + 1,1)*ys; + + win_mat = (-win:win)'*ones(1,Np); + + + xs_mat2 = round(xs_mat - win_mat * lambda(1)); + ys_mat2 = round(ys_mat - win_mat * lambda(2)); + + ind_mat = (xs_mat2 - 1) * ny + ys_mat2; + + ima_patch = zeros(2*win + 1,Np); + + ima_patch(:) = I(ind_mat(:)); + + %ima2 = ima_patch(:,win+1:end-win); + + filtk = [ones(win,Np);zeros(1,Np);-ones(win,Np)]; + + out_f = sum(filtk.*ima_patch); + + out_f_f = conv2(out_f,[1/4 1/2 1/4],'same'); + + out_f_f = out_f_f(win+1:end-win); + + ns = length(find(((out_f_f(2:end)>=0)&(out_f_f(1:end-1)<0)) | ((out_f_f(2:end)<=0)&(out_f_f(1:end-1)>0))))+1; + + + + +return; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/data_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/data_calib.m new file mode 100755 index 0000000..318ec15 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/data_calib.m @@ -0,0 +1,89 @@ +%%% This script alets the user enter the name of the images (base name, numbering scheme,... + +dir; + +%disp('Camera Calibration using multiple images of a planar checkerboard pattern'); +%disp('Model: 2 focals, 2 radial dist. coeff., 2 tangential dist. coeff. and principle point'); +%disp(' => 8DOF intrinsic model ([Heikkila and Silven, University of Oulu])'); + +fprintf(1,'\n'); +calib_name = input('Basename camera calibration images (without number nor suffix): ','s'); + +format_image = '0'; + +while format_image == '0', + + format_image = input('Image format: ([]=''r''=''ras'', ''b''=''bmp'', ''t''=''tif'', ''p''=''pgm'', ''j''=''jpg'') ','s'); + + if isempty(format_image), + format_image = 'ras'; + end; + + if lower(format_image(1)) == 'b', + format_image = 'bmp'; + else + if lower(format_image(1)) == 't', + format_image = 'tif'; + else + if lower(format_image(1)) == 'p', + format_image = 'pgm'; + else + if lower(format_image(1)) == 'j', + format_image = 'jpg'; + else + if lower(format_image(1)) == 'r', + format_image = 'ras'; + else + disp('Invalid image format'); + format_image = '0'; % Ask for format once again + end; + end; + end; + end; + end; + +end; + + +n_ima = 1000; +while n_ima > 30, + n_ima = input('Number of calibration images: '); + n_ima = round(n_ima); +end; + +type_numbering = input('Type of numbering (ex: []=4,other=04): '); + +type_numbering = ~isempty(type_numbering); + +if type_numbering, + + N_slots = input('Number of spaces for numbers? (ex: 2 -> 04, 3 -> 004), ([]=3) '); + + if isempty(N_slots), N_slots = 3; end; + +else + + N_slots = 1; % not used anyway, but useful for saving + +end; + + +first_num = input('First image number? (0,1,2...) ([]=0) '); + +if isempty(first_num), first_num = 0; end; + +image_numbers = first_num:n_ima-1+first_num; + + +%%% By default, all the images are active for calibration: + +active_images = ones(1,n_ima); + +%string_save = 'save calib_data n_ima type_numbering N_slots image_numbers format_image calib_name first_num'; + +%eval(string_save); + +% Reading images: + +ima_read_calib; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/error_analysis.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/error_analysis.m new file mode 100755 index 0000000..85feac5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/error_analysis.m @@ -0,0 +1,182 @@ +%%% ERROR_ANALYSIS +%%% This simulation helps coputing the acturacies of calibration +%%% Run it after the main calibration + + + +N_runs = 200; + +%N_ima_active = 4; + +saving = 1; + +if 1, %~exist('fc_list'), % initialization + + % Initialization: + + load Calib_Results; + check_active_images; + + fc_list = []; + cc_list = []; + kc_list = []; + active_images_list = []; + + + for kk=1:n_ima, + + eval(['omc_list_' num2str(kk) ' = [];']); + eval(['Tc_list_' num2str(kk) ' = [];']); + + end; + + %sx = median(abs(ex(1,:)))*1.4836; + %sy = median(abs(ex(2,:)))*1.4836; + + sx = std(ex(1,:)); + sy = std(ex(2,:)); + + % Saving the feature locations: + + for kk = 1:n_ima, + + eval(['x_save_' num2str(kk) ' = x_' num2str(kk) ';']); + eval(['y_save_' num2str(kk) ' = y_' num2str(kk) ';']); + + end; + + active_images_save = active_images; + ind_active_save = ind_active; + + fc_save = fc; + cc_save = cc; + kc_save = kc; + KK_save = KK; + + +end; + + + + +%%% The main loop: + + +for ntrial = 1:N_runs, + + fprintf(1,'\nRun number: %d\n',ntrial); + fprintf(1, '----------\n'); + + for kk = 1:n_ima, + + eval(['y_kk = y_save_' num2str(kk) ';']) + + if active_images(kk) & ~isnan(y_kk(1,1)), + + Nkk = size(y_kk,2); + + x_kk_new = y_kk + [sx * randn(1,Nkk);sy*randn(1,Nkk)]; + + eval(['x_' num2str(kk) ' = x_kk_new;']); + + end; + + end; + + N_active = length(ind_active_save); + junk = randn(1,N_active); + [junk,junk2] = sort(junk); + + active_images = zeros(1,n_ima); + active_images(ind_active_save(junk2(1:N_ima_active))) = ones(1,N_ima_active); + + fc = fc_save; + cc = cc_save; + kc = kc_save; + KK = KK_save; + + go_calib_optim; + + fc_list = [fc_list fc]; + cc_list = [cc_list cc]; + kc_list = [kc_list kc]; + active_images_list = [active_images_list active_images']; + + for kk=1:n_ima, + + eval(['omc_list_' num2str(kk) ' = [ omc_list_' num2str(kk) ' omc_' num2str(kk) ' ];']); + eval(['Tc_list_' num2str(kk) ' = [ Tc_list_' num2str(kk) ' Tc_' num2str(kk) ' ];']); + + end; + +end; + + + + +if 0, + +% Restoring the feature locations: + +for kk = 1:n_ima, + + eval(['x_' num2str(kk) ' = x_save_' num2str(kk) ';']); + +end; + +fprintf(1,'\nFinal run (with the real data)\n'); +fprintf(1, '------------------------------\n'); + +active_images = active_images_save; +ind_active = ind_active_save; + +go_calib_optim; + +fc_list = [fc_list fc]; +cc_list = [cc_list cc]; +kc_list = [kc_list kc]; +active_images_list = [active_images_list active_images']; + +for kk=1:n_ima, + + eval(['omc_list_' num2str(kk) ' = [ omc_list_' num2str(kk) ' omc_' num2str(kk) ' ];']); + eval(['Tc_list_' num2str(kk) ' = [ Tc_list_' num2str(kk) ' Tc_' num2str(kk) ' ];']); + +end; + +end; + + + + + +if saving, + +disp(['Save Calibration accuracy results under Calib_Accuracies_' num2str(N_ima_active) '.mat']); + +string_save = ['save Calib_Accuracies_' num2str(N_ima_active) ' active_images n_ima N_ima_active N_runs active_images_list fc cc kc fc_list cc_list kc_list']; + +for kk = 1:n_ima, + string_save = [string_save ' Tc_list_' num2str(kk) ' omc_list_' num2str(kk) ' Tc_' num2str(kk) ' omc_' num2str(kk) ]; +end; + +eval(string_save); + +end; + + +return; + +std(fc_list') + +std(cc_list') + +std(kc_list') + +for kk = 1:n_ima, + + eval(['std(Tc_list_' num2str(kk) ''')']) + +end; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ext_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ext_calib.m new file mode 100755 index 0000000..d41d068 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ext_calib.m @@ -0,0 +1,130 @@ + +%%%%%%%%%%%%%%%%%%%% SHOW EXTRINSIC RESULTS %%%%%%%%%%%%%%%%%%%%%%%% + +check_active_images; + +if ~exist(['omc_' num2str(ind_active(1))]), + fprintf(1,'Need to calibrate before showing extrinsic results. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + +%if ~exist('no_grid'), + no_grid = 0; +%end; + +if ~exist(['n_sq_x_' num2str(ind_active(1))]), + no_grid = 1; +end; + + +if 0, + +err_std = std(ex'); + +fprintf(1,'\n\nCalibration results without principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); + +end; + + +% Color code for each image: + +colors = 'brgkcm'; + + +%%% Show the extrinsic parameters + +if ~exist('dX'), + eval(['dX = norm(Tc_' num2str(ind_active(1)) ')/10;']); + dY = dX; +end; + +IP = 5*dX*([0 nx-1 nx-1 0 0 ; 0 0 ny-1 ny-1 0;1 1 1 1 1] - [cc;0]*ones(1,5)) ./ ([fc;1]*ones(1,5)); +BASE = 5*dX*([0 1 0 0 0 0;0 0 0 1 0 0;0 0 0 0 0 1]); +IP = reshape([IP;BASE(:,1)*ones(1,5);IP],3,15); + +figure(4); +[a,b] = view; + +figure(4); +plot3(BASE(1,:),BASE(3,:),-BASE(2,:),'b-','linewidth',2'); +hold on; +plot3(IP(1,:),IP(3,:),-IP(2,:),'r-','linewidth',2); +text(6*dX,0,0,'X_c'); +text(-dX,5*dX,0,'Z_c'); +text(0,0,-6*dX,'Y_c'); +text(-dX,-dX,dX,'O_c'); + + +for kk = 1:n_ima, + + if active_images(kk); + + eval(['XX_kk = X_' num2str(kk) ';']); + eval(['omc_kk = omc_' num2str(kk) ';']); + eval(['Tc_kk = Tc_' num2str(kk) ';']); + N_kk = size(XX_kk,2); + + if ~exist(['n_sq_x_' num2str(kk)]), + no_grid = 1; + end; + + if ~no_grid, + eval(['n_sq_x = n_sq_x_' num2str(kk) ';']); + eval(['n_sq_y = n_sq_y_' num2str(kk) ';']); + if (N_kk ~= ((n_sq_x+1)*(n_sq_y+1))), + no_grid = 1; + end; + end; + + if ~isnan(omc_kk(1,1)), + + R_kk = rodrigues(omc_kk); + + YY_kk = R_kk * XX_kk + Tc_kk * ones(1,length(XX_kk)); + + uu = [-dX;-dY;0]/2; + uu = R_kk * uu + Tc_kk; + + if ~no_grid, + YYx = zeros(n_sq_x+1,n_sq_y+1); + YYy = zeros(n_sq_x+1,n_sq_y+1); + YYz = zeros(n_sq_x+1,n_sq_y+1); + + YYx(:) = YY_kk(1,:); + YYy(:) = YY_kk(2,:); + YYz(:) = YY_kk(3,:); + + %keyboard; + + figure(4); + hhh= mesh(YYx,YYz,-YYy); + set(hhh,'edgecolor',colors(rem(kk-1,6)+1),'linewidth',1); %,'facecolor','none'); + %plot3(YY_kk(1,:),YY_kk(3,:),-YY_kk(2,:),['o' colors(rem(kk-1,6)+1)]); + text(uu(1),uu(3),-uu(2),num2str(kk),'fontsize',14,'color',colors(rem(kk-1,6)+1)); + else + + figure(4); + plot3(YY_kk(1,:),YY_kk(3,:),-YY_kk(2,:),['.' colors(rem(kk-1,6)+1)]); + text(uu(1),uu(3),-uu(2),num2str(kk),'fontsize',14,'color',colors(rem(kk-1,6)+1)); + + end; + + end; + + end; + +end; + +figure(4);rotate3d on; +axis('equal'); +title('Extrinsic parameters'); +%view(60,30); +view(a,b); +hold off; + +set(4,'Name','3D','NumberTitle','off'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_grid.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_grid.m new file mode 100755 index 0000000..1e3cbdb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_grid.m @@ -0,0 +1,227 @@ +function [x,X,n_sq_x,n_sq_y,ind_orig,ind_x,ind_y] = extract_grid(I,wintx,winty,fc,cc,kc); + +map = gray(256); + + figure(2); + image(I); + colormap(map); + + + if nargin < 2, + + disp('Window size for corner finder (wintx and winty):'); + wintx = input('wintx ([] = 5) = '); + if isempty(wintx), wintx = 5; end; + wintx = round(wintx); + winty = input('winty ([] = 5) = '); + if isempty(winty), winty = 5; end; + winty = round(winty); + + fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + + end; + + + + title('Click on the four extreme corners of the rectangular pattern...'); + + disp('Click on the four extreme corners of the rectangular complete pattern...'); + + [x,y] = ginput3(4); + + [Xc,good,bad,type] = cornerfinder([x';y'],I,winty,wintx); % the four corners + + x = Xc(1,:)'; + y = Xc(2,:)'; + + [y,indy] = sort(y); + x = x(indy); + + if (x(2) > x(1)), + x4 = x(1);y4 = y(1); x3 = x(2); y3 = y(2); + else + x4 = x(2);y4 = y(2); x3 = x(1); y3 = y(1); + end; + if (x(3) > x(4)), + x2 = x(3);y2 = y(3); x1 = x(4); y1 = y(4); + else + x2 = x(4);y2 = y(4); x1 = x(3); y1 = y(3); + end; + + x = [x1;x2;x3;x4]; + y = [y1;y2;y3;y4]; + + + figure(2); hold on; + plot([x;x(1)],[y;y(1)],'g-'); + plot(x,y,'og'); + hx=text((x(4)+x(3))/2,(y(4)+y(3))/2 - 20,'X'); + set(hx,'color','g','Fontsize',14); + hy=text((x(4)+x(1))/2-20,(y(4)+y(1))/2,'Y'); + set(hy,'color','g','Fontsize',14); + hold off; + + + % Try to automatically count the number of squares in the grid + + n_sq_x1 = count_squares(I,x1,y1,x2,y2,wintx); + n_sq_x2 = count_squares(I,x3,y3,x4,y4,wintx); + n_sq_y1 = count_squares(I,x2,y2,x3,y3,wintx); + n_sq_y2 = count_squares(I,x4,y4,x1,y1,wintx); + + + + % If could not count the number of squares, enter manually + + if (n_sq_x1~=n_sq_x2)|(n_sq_y1~=n_sq_y2), + + + disp('Could not count the number of squares in the grid. Enter manually.'); + n_sq_x = input('Number of squares along the X direction ([]=10) = '); %6 + if isempty(n_sq_x), n_sq_x = 10; end; + n_sq_y = input('Number of squares along the Y direction ([]=10) = '); %6 + if isempty(n_sq_y), n_sq_y = 10; end; + + else + + n_sq_x = n_sq_x1; + n_sq_y = n_sq_y1; + + end; + + + % Enter the size of each square + + dX = input(['Size dX of each square along the X direction ([]=3cm) = ']); + dY = input(['Size dY of each square along the Y direction ([]=3cm) = ']); + if isempty(dX), dX = 3; end; + if isempty(dY), dY = 3; end; + + + + % Compute the inside points through computation of the planar homography (collineation) + + a00 = [x(1);y(1);1]; + a10 = [x(2);y(2);1]; + a11 = [x(3);y(3);1]; + a01 = [x(4);y(4);1]; + + + % Compute the planart collineation: (return the normalization matrice as well) + + [Homo,Hnorm,inv_Hnorm] = compute_homography ([a00 a10 a11 a01],[0 1 1 0;0 0 1 1;1 1 1 1]); + + + % Build the grid using the planar collineation: + + x_l = ((0:n_sq_x)'*ones(1,n_sq_y+1))/n_sq_x; + y_l = (ones(n_sq_x+1,1)*(0:n_sq_y))/n_sq_y; + pts = [x_l(:) y_l(:) ones((n_sq_x+1)*(n_sq_y+1),1)]'; + + XX = Homo*pts; + XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); + + + % Complete size of the rectangle + + W = n_sq_x*dX; + L = n_sq_y*dY; + + + + if nargin < 6, + + %%%%%%%%%%%%%%%%%%%%%%%% ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + figure(2); + hold on; + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be close to the image corners'); + hold off; + + disp('If the guessed grid corners (red crosses on the image) are not close to the actual corners,'); + disp('it is necessary to enter an initial guess for the radial distortion factor kc (useful for subpixel detection)'); + quest_distort = input('Need of an initial guess for distortion? ([]=no, other=yes) '); + + quest_distort = ~isempty(quest_distort); + + if quest_distort, + % Estimation of focal length: + c_g = [size(I,2);size(I,1)]/2 + .5; + f_g = Distor2Calib(0,[[x(1) x(2) x(4) x(3)] - c_g(1);[y(1) y(2) y(4) y(3)] - c_g(2)],1,1,4,W,L,[-W/2 W/2 W/2 -W/2;L/2 L/2 -L/2 -L/2; 0 0 0 0],100,1,1); + f_g = mean(f_g); + script_fit_distortion; + end; + %%%%%%%%%%%%%%%%%%%%% END ADDITIONAL STUFF IN THE CASE OF HIGHLY DISTORTED IMAGES %%%%%%%%%%%%% + + else + + xy_corners_undist = comp_distortion_oulu([(x' - cc(1))/fc(1);(y'-cc(2))/fc(1)],kc); + + xu = xy_corners_undist(1,:)'; + yu = xy_corners_undist(2,:)'; + + [XXu] = projectedGrid ( [xu(1);yu(1)], [xu(2);yu(2)],[xu(3);yu(3)], [xu(4);yu(4)],n_sq_x+1,n_sq_y+1); % The full grid + + r2 = sum(XXu.^2); + XX = (ones(2,1)*(1 + kc(1) * r2 + kc(2) * (r2.^2))) .* XXu; + XX(1,:) = fc(1)*XX(1,:)+cc(1); + XX(2,:) = fc(2)*XX(2,:)+cc(2); + + end; + + + Np = (n_sq_x+1)*(n_sq_y+1); + + disp('Corner extraction...'); + + grid_pts = cornerfinder(XX,I,winty,wintx); %%% Finds the exact corners at every points! + + grid_pts = grid_pts - 1; % subtract 1 to bring the origin to (0,0) instead of (1,1) in matlab (not necessary in C) + + ind_corners = [1 n_sq_x+1 (n_sq_x+1)*n_sq_y+1 (n_sq_x+1)*(n_sq_y+1)]; % index of the 4 corners + ind_orig = (n_sq_x+1)*n_sq_y + 1; + xorig = grid_pts(1,ind_orig); + yorig = grid_pts(2,ind_orig); + dxpos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig+1)]'); + dypos = mean([grid_pts(:,ind_orig) grid_pts(:,ind_orig-n_sq_x-1)]'); + + + ind_x = (n_sq_x+1)*(n_sq_y + 1); + ind_y = 1; + + x_box_kk = [grid_pts(1,:)-(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)+(wintx+.5);grid_pts(1,:)-(wintx+.5);grid_pts(1,:)-(wintx+.5)]; + y_box_kk = [grid_pts(2,:)-(winty+.5);grid_pts(2,:)-(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)+(winty+.5);grid_pts(2,:)-(winty+.5)]; + + + figure(3); + image(I); colormap(map); hold on; + plot(grid_pts(1,:)+1,grid_pts(2,:)+1,'r+'); + plot(x_box_kk+1,y_box_kk+1,'-b'); + plot(grid_pts(1,ind_corners)+1,grid_pts(2,ind_corners)+1,'mo'); + plot(xorig+1,yorig+1,'*m'); + h = text(xorig-15,yorig-15,'O'); + set(h,'Color','m','FontSize',14); + h2 = text(dxpos(1)-10,dxpos(2)-10,'dX'); + set(h2,'Color','g','FontSize',14); + h3 = text(dypos(1)-25,dypos(2)-3,'dY'); + set(h3,'Color','g','FontSize',14); + xlabel('Xc (in camera frame)'); + ylabel('Yc (in camera frame)'); + title('Extracted corners'); + zoom on; + drawnow; + hold off; + + + Xi = reshape(([0:n_sq_x]*dX)'*ones(1,n_sq_y+1),Np,1)'; + Yi = reshape(ones(n_sq_x+1,1)*[n_sq_y:-1:0]*dY,Np,1)'; + Zi = zeros(1,Np); + + Xgrid = [Xi;Yi;Zi]; + + + % All the point coordinates (on the image, and in 3D) - for global optimization: + + x = grid_pts; + X = Xgrid; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters.m new file mode 100755 index 0000000..8e0e1f1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters.m @@ -0,0 +1,46 @@ + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +check_active_images; + +fc = solution(1:2); +kc = solution(3:6); +cc = solution(6*n_ima + 4 +3:6*n_ima + 5 +3); + +% Calibration matrix: + +KK = [fc(1) 0 cc(1);0 fc(2) cc(2); 0 0 1]; +inv_KK = inv(KK); + +% Extract the extrinsic paramters, and recomputer the collineations + +for kk = 1:n_ima, + + if active_images(kk), + + omckk = solution(4+6*(kk-1) + 3:6*kk + 3); + Tckk = solution(6*kk+1 + 3:6*kk+3 + 3); + + Rckk = rodrigues(omckk); + + Hkk = KK * [Rckk(:,1) Rckk(:,2) Tckk]; + + Hkk = Hkk / Hkk(3,3); + + else + + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + Rckk = NaN*ones(3,3); + Hkk = NaN*ones(3,3); + + end; + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Rc_' num2str(kk) ' = Rckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + eval(['H_' num2str(kk) '= Hkk;']); + +end; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters3D.m new file mode 100755 index 0000000..841c6ab --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extract_parameters3D.m @@ -0,0 +1,36 @@ + +%%% Extraction of the final intrinsic and extrinsic paramaters: + + +fc = solution(1:2); +kc = solution(3:6); +cc = solution(6*n_ima + 4 +3:6*n_ima + 5 +3); + +% Calibration matrix: + +KK = [fc(1) 0 cc(1);0 fc(2) cc(2); 0 0 1]; +inv_KK = inv(KK); + +% Extract the extrinsic paramters, and recomputer the collineations + +for kk = 1:n_ima, + + omckk = solution(4+6*(kk-1) + 3:6*kk + 3); + + Tckk = solution(6*kk+1 + 3:6*kk+3 + 3); + + Rckk = rodrigues(omckk); + + Hlkk = KK * [Rckk(:,1) Rckk(:,2) Tckk]; + + Hlkk = Hlkk / Hlkk(3,3); + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Rc_' num2str(kk) ' = Rckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + + eval(['Hl_' num2str(kk) '=Hlkk;']); + +end; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extrinsic_computation.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extrinsic_computation.m new file mode 100755 index 0000000..8cf10db --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/extrinsic_computation.m @@ -0,0 +1,173 @@ +%%% INPUT THE IMAGE FILE NAME: + +dir; + +fprintf(1,'\n'); +disp('Computation of the extrinsic parameters from an image of a pattern'); +disp('The intrinsic camera parameters are assumed to be known (previously computed)'); + +fprintf(1,'\n'); +image_name = input('Image name (full name without extension): ','s'); + +format_image2 = '0'; + +while format_image2 == '0', + + format_image2 = input('Image format: ([]=''r''=''ras'', ''b''=''bmp'', ''t''=''tif'', ''p''=''pgm'', ''j''=''jpg'') ','s'); + + if isempty(format_image2), + format_image2 = 'ras'; + end; + + if lower(format_image2(1)) == 'b', + format_image2 = 'bmp'; + else + if lower(format_image2(1)) == 't', + format_image2 = 'tif'; + else + if lower(format_image2(1)) == 'p', + format_image2 = 'pgm'; + else + if lower(format_image2(1)) == 'j', + format_image2 = 'jpg'; + else + if lower(format_image2(1)) == 'r', + format_image2 = 'ras'; + else + disp('Invalid image format'); + format_image2 = '0'; % Ask for format once again + end; + end; + end; + end; + end; +end; + +ima_name = [image_name '.' format_image]; + + + +%%% READ IN IMAGE: + +if format_image(1) == 'p', + I = double(pgmread(ima_name)); +else + if format_image(1) == 'r', + I = readras(ima_name); + else + I = double(imread(ima_name)); + end; +end; + +if size(I,3)>1, + I = I(:,:,2); +end; + + +%%% EXTRACT GRID CORNERS: + +fprintf(1,'\nExtraction of the grid corners on the image\n'); + +disp('Window size for corner finder (wintx and winty):'); +wintx = input('wintx ([] = 5) = '); +if isempty(wintx), wintx = 5; end; +wintx = round(wintx); +winty = input('winty ([] = 5) = '); +if isempty(winty), winty = 5; end; +winty = round(winty); + +fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + +[x_ext,X_ext,n_sq_x,n_sq_y,ind_orig,ind_x,ind_y] = extract_grid(I,wintx,winty,fc,cc,kc); + + + +%%% Computation of the Extrinsic Parameters attached to the grid: + +[omc_ext,Tc_ext,Rc_ext,H_ext] = compute_extrinsic(x_ext,X_ext,fc,cc,kc); + + +%%% Reproject the points on the image: + +[x_reproj] = project_points(X_ext,omc_ext,Tc_ext,fc,cc,kc); + +err_reproj = x_ext - x_reproj; + +err_std2 = std(err_reproj')'; + + +Basis = [X_ext(:,[ind_orig ind_x ind_orig ind_y ind_orig ])]; + +VX = Basis(:,2) - Basis(:,1); +VY = Basis(:,4) - Basis(:,1); + +nX = norm(VX); +nY = norm(VY); + +VZ = min(nX,nY) * cross(VX/nX,VY/nY); + +Basis = [Basis VZ]; + +[x_basis] = project_points(Basis,omc_ext,Tc_ext,fc,cc,kc); + +dxpos = (x_basis(:,2) + x_basis(:,1))/2; +dypos = (x_basis(:,4) + x_basis(:,3))/2; +dzpos = (x_basis(:,6) + x_basis(:,5))/2; + + + +figure(2); +image(I); +colormap(gray(256)); +hold on; +plot(x_ext(1,:)+1,x_ext(2,:)+1,'r+'); +plot(x_reproj(1,:)+1,x_reproj(2,:)+1,'yo'); +h = text(x_ext(1,ind_orig)-25,x_ext(2,ind_orig)-25,'O'); +set(h,'Color','g','FontSize',14); +h2 = text(dxpos(1)+1,dxpos(2)-30,'X'); +set(h2,'Color','g','FontSize',14); +h3 = text(dypos(1)-30,dypos(2)+1,'Y'); +set(h3,'Color','g','FontSize',14); +h4 = text(dzpos(1)-10,dzpos(2)-20,'Z'); +set(h4,'Color','g','FontSize',14); +plot(x_basis(1,:)+1,x_basis(2,:)+1,'g-','linewidth',2); +title('Image points (+) and reprojected grid points (o)'); +hold off; + + +fprintf(1,'\n\nExtrinsic parameters:\n\n'); +fprintf(1,'Translation vector: Tc_ext = [ %3.6f \t %3.6f \t %3.6f ]\n',Tc_ext); +fprintf(1,'Rotation vector: omc_ext = [ %3.6f \t %3.6f \t %3.6f ]\n',omc_ext); +fprintf(1,'Rotation matrix: Rc_ext = [ %3.6f \t %3.6f \t %3.6f\n',Rc_ext(1,:)'); +fprintf(1,' %3.6f \t %3.6f \t %3.6f\n',Rc_ext(2,:)'); +fprintf(1,' %3.6f \t %3.6f \t %3.6f ]\n',Rc_ext(3,:)'); +fprintf(1,'Pixel error: err = [ %3.5f \t %3.5f ]\n\n',err_std2); + + + + + +return; + + +% Stores the results: + +kk = 1; + +% Stores location of grid wrt camera: + +eval(['omc_' num2str(kk) ' = omc_ext;']); +eval(['Tc_' num2str(kk) ' = Tc_ext;']); + +% Stores the projected points: + +eval(['y_' num2str(kk) ' = x_reproj;']); +eval(['X_' num2str(kk) ' = X_ext;']); +eval(['x_' num2str(kk) ' = x_ext;']); + + +% Organize the points in a grid: + +eval(['n_sq_x_' num2str(kk) ' = n_sq_x;']); +eval(['n_sq_y_' num2str(kk) ' = n_sq_y;']); + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ginput3.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ginput3.m new file mode 100755 index 0000000..56fe496 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ginput3.m @@ -0,0 +1,216 @@ +function [out1,out2,out3] = ginput2(arg1) +%GINPUT Graphical input from mouse. +% [X,Y] = GINPUT(N) gets N points from the current axes and returns +% the X- and Y-coordinates in length N vectors X and Y. The cursor +% can be positioned using a mouse (or by using the Arrow Keys on some +% systems). Data points are entered by pressing a mouse button +% or any key on the keyboard except carriage return, which terminates +% the input before N points are entered. +% +% [X,Y] = GINPUT gathers an unlimited number of points until the +% return key is pressed. +% +% [X,Y,BUTTON] = GINPUT(N) returns a third result, BUTTON, that +% contains a vector of integers specifying which mouse button was +% used (1,2,3 from left) or ASCII numbers if a key on the keyboard +% was used. + +% Copyright (c) 1984-96 by The MathWorks, Inc. +% $Revision: 5.18 $ $Date: 1996/11/10 17:48:08 $ + +% Fixed version by Jean-Yves Bouguet to have a cross instead of 2 lines +% More visible for images + +P = NaN*ones(16,16); +P(1:15,1:15) = 2*ones(15,15); +P(2:14,2:14) = ones(13,13); +P(3:13,3:13) = NaN*ones(11,11); +P(6:10,6:10) = 2*ones(5,5); +P(7:9,7:9) = 1*ones(3,3); + +out1 = []; out2 = []; out3 = []; y = []; +c = computer; +if ~strcmp(c(1:2),'PC') & ~strcmp(c(1:2),'MA') + tp = get(0,'TerminalProtocol'); +else + tp = 'micro'; +end + +if ~strcmp(tp,'none') & ~strcmp(tp,'x') & ~strcmp(tp,'micro'), + if nargout == 1, + if nargin == 1, + eval('out1 = trmginput(arg1);'); + else + eval('out1 = trmginput;'); + end + elseif nargout == 2 | nargout == 0, + if nargin == 1, + eval('[out1,out2] = trmginput(arg1);'); + else + eval('[out1,out2] = trmginput;'); + end + if nargout == 0 + out1 = [ out1 out2 ]; + end + elseif nargout == 3, + if nargin == 1, + eval('[out1,out2,out3] = trmginput(arg1);'); + else + eval('[out1,out2,out3] = trmginput;'); + end + end +else + + fig = gcf; + figure(gcf); + + if nargin == 0 + how_many = -1; + b = []; + else + how_many = arg1; + b = []; + if isstr(how_many) ... + | size(how_many,1) ~= 1 | size(how_many,2) ~= 1 ... + | ~(fix(how_many) == how_many) ... + | how_many < 0 + error('Requires a positive integer.') + end + if how_many == 0 + ptr_fig = 0; + while(ptr_fig ~= fig) + ptr_fig = get(0,'PointerWindow'); + end + scrn_pt = get(0,'PointerLocation'); + loc = get(fig,'Position'); + pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)]; + out1 = pt(1); y = pt(2); + elseif how_many < 0 + error('Argument must be a positive integer.') + end + end + +pointer = get(gcf,'pointer'); + +set(gcf,'Pointer','custom','PointerShapeCData',P,'PointerShapeHotSpot',[8,8]); +%set(gcf,'pointer','crosshair'); + fig_units = get(fig,'units'); + char = 0; + + while how_many ~= 0 + % Use no-side effect WAITFORBUTTONPRESS + waserr = 0; + eval('keydown = wfbp;', 'waserr = 1;'); + if(waserr == 1) + if(ishandle(fig)) + set(fig,'pointer',pointer,'units',fig_units); + error('Interrupted'); + else + error('Interrupted by figure deletion'); + end + end + + ptr_fig = get(0,'CurrentFigure'); + if(ptr_fig == fig) + if keydown + char = get(fig, 'CurrentCharacter'); + button = abs(get(fig, 'CurrentCharacter')); + scrn_pt = get(0, 'PointerLocation'); + set(fig,'units','pixels') + loc = get(fig, 'Position'); + pt = [scrn_pt(1) - loc(1), scrn_pt(2) - loc(2)]; + set(fig,'CurrentPoint',pt); + else + button = get(fig, 'SelectionType'); + if strcmp(button,'open') + button = b(length(b)); + elseif strcmp(button,'normal') + button = 1; + elseif strcmp(button,'extend') + button = 2; + elseif strcmp(button,'alt') + button = 3; + else + error('Invalid mouse selection.') + end + end + pt = get(gca, 'CurrentPoint'); + + how_many = how_many - 1; + + if(char == 13) % & how_many ~= 0) + % if the return key was pressed, char will == 13, + % and that's our signal to break out of here whether + % or not we have collected all the requested data + % points. + % If this was an early breakout, don't include + % the key info in the return arrays. + % We will no longer count it if it's the last input. + break; + end + + out1 = [out1;pt(1,1)]; + y = [y;pt(1,2)]; + b = [b;button]; + end + end + + set(fig,'pointer',pointer,'units',fig_units); + + if nargout > 1 + out2 = y; + if nargout > 2 + out3 = b; + end + else + out1 = [out1 y]; + end + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function key = wfbp +%WFBP Replacement for WAITFORBUTTONPRESS that has no side effects. + +% Remove figure button functions +fprops = {'windowbuttonupfcn','buttondownfcn', ... + 'windowbuttondownfcn','windowbuttonmotionfcn'}; +fig = gcf; +fvals = get(fig,fprops); +set(fig,fprops,{'','','',''}) + +% Remove all other buttondown functions +ax = findobj(fig,'type','axes'); +if isempty(ax) + ch = {}; +else + ch = get(ax,{'Children'}); +end +for i=1:length(ch), + ch{i} = ch{i}(:)'; +end +h = [ax(:)',ch{:}]; +vals = get(h,{'buttondownfcn'}); +mt = repmat({''},size(vals)); +set(h,{'buttondownfcn'},mt); + +% Now wait for that buttonpress, and check for error conditions +waserr = 0; +eval(['if nargout==0,', ... + ' waitforbuttonpress,', ... + 'else,', ... + ' keydown = waitforbuttonpress;',... + 'end' ], 'waserr = 1;'); + +% Put everything back +if(ishandle(fig)) + set(fig,fprops,fvals) + set(h,{'buttondownfcn'},vals) +end + +if(waserr == 1) + error('Interrupted'); +end + +if nargout>0, key = keydown; end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim.m new file mode 100755 index 0000000..6eb1c82 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim.m @@ -0,0 +1,60 @@ +%go_calib_optim +% +%Main calibration function. Computes the intrinsic andextrinsic parameters. +%Runs as a script. +% +%INPUT: x_1,x_2,x_3,...: Feature locations on the images +% X_1,X_2,X_3,...: Corresponding grid coordinates +% +%OUTPUT: fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% KK: The camera matrix (containing fc and cc) +% omc_1,omc_2,omc_3,...: 3D rotation vectors attached to the grid positions in space +% Tc_1,Tc_2,Tc_3,...: 3D translation vectors attached to the grid positions in space +% Rc_1,Rc_2,Rc_3,...: 3D rotation matrices corresponding to the omc vectors +% +%Method: Minimizes the pixel reprojection error in the least squares sense over the intrinsic +% camera parameters, and the extrinsic parameters (3D locations of the grids in space) +% +%Note: If the intrinsic camera parameters (fc, cc, kc) do not exist before, they are initialized through +% the function init_intrinsic_param.m. Otherwise, the variables in memory are used as initial guesses. +% +%Note: The row vector active_images consists of zeros and ones. To deactivate an image, set the +% corresponding entry in the active_images vector to zero. +% +%VERY IMPORTANT: This function works for 2D and 3D calibration rigs, except for init_intrinsic_param.m +%that is so far implemented to work only with 2D rigs. +%In the future, a more general function will be there. +%For now, if using a 3D calibration rig, set quick_init to 1 for an easy initialization of the focal length + + +desactivated_images = []; + + +go_calib_optim_iter; + + +if ~isempty(desactivated_images), + + param_list_save = param_list; + + fprintf(1,'\nNew optimization including the images that have been deactivated during the previous optimization.\n'); + active_images(desactivated_images) = ones(1,length(desactivated_images)); + desactivated_images = []; + + go_calib_optim_iter; + + if ~isempty(desactivated_images), + fprintf(1,['List of images left desactivated: ' num2str(desactivated_images) '\n' ] ); + end; + + param_list = [param_list_save(:,1:end-1) param_list]; + +end; + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +%graphout_calib; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim3D.m new file mode 100755 index 0000000..8cc5e30 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim3D.m @@ -0,0 +1,264 @@ +% Simplified version of go_calib.m + + +if ~exist('x_1'), + click_calib; +end; + + +fprintf(1,'\nMain calibration procedure\n'); + +% initial guess for principal point and distortion: +c_init = [nx;ny]/2 - 0.5; % initialize at the center of the image +k_init = [0;0;0;0]; % initialize to zero (no distortion) + + +% Compute explicitely the focal lentgh using all the (mutually orthogonal) vanishing points +% note: The vanihing points are hidden in the planar collineations H_kk + +A = []; +b = []; + +% matrix that subtract the principal point: +Sub_cc = [1 0 -c_init(1);0 1 -c_init(2);0 0 1]; + + +for kk=1:n_ima, + + % left Pattern: + + eval(['Hlkk = Hl_' num2str(kk) ';']); + + Hlkk = Sub_cc * Hlkk; + + % Extract vanishing points (direct and diagonals): + + Vl_hori_pix = Hlkk(:,1); + Vl_vert_pix = Hlkk(:,2); + Vl_diag1_pix = (Hlkk(:,1)+Hlkk(:,2))/2; + Vl_diag2_pix = (Hlkk(:,1)-Hlkk(:,2))/2; + + Vl_hori_pix = Vl_hori_pix/norm(Vl_hori_pix); + Vl_vert_pix = Vl_vert_pix/norm(Vl_vert_pix); + Vl_diag1_pix = Vl_diag1_pix/norm(Vl_diag1_pix); + Vl_diag2_pix = Vl_diag2_pix/norm(Vl_diag2_pix); + + al1 = Vl_hori_pix(1); + bl1 = Vl_hori_pix(2); + cl1 = Vl_hori_pix(3); + + al2 = Vl_vert_pix(1); + bl2 = Vl_vert_pix(2); + cl2 = Vl_vert_pix(3); + + al3 = Vl_diag1_pix(1); + bl3 = Vl_diag1_pix(2); + cl3 = Vl_diag1_pix(3); + + al4 = Vl_diag2_pix(1); + bl4 = Vl_diag2_pix(2); + cl4 = Vl_diag2_pix(3); + + % right Pattern: + + eval(['Hrkk = Hr_' num2str(kk) ';']); + + Hrkk = Sub_cc * Hrkk; + + % Extract vanishing points (direct and diagonals): + + Vr_hori_pix = Hrkk(:,1); + Vr_vert_pix = Hrkk(:,2); + Vr_diag1_pix = (Hrkk(:,1)+Hrkk(:,2))/2; + Vr_diag2_pix = (Hrkk(:,1)-Hrkk(:,2))/2; + + Vr_hori_pix = Vr_hori_pix/norm(Vl_hori_pix); + Vr_vert_pix = Vr_vert_pix/norm(Vl_vert_pix); + Vr_diag1_pix = Vr_diag1_pix/norm(Vr_diag1_pix); + Vr_diag2_pix = Vr_diag2_pix/norm(Vr_diag2_pix); + + ar1 = Vr_hori_pix(1); + br1 = Vr_hori_pix(2); + cr1 = Vr_hori_pix(3); + + ar2 = Vr_vert_pix(1); + br2 = Vr_vert_pix(2); + cr2 = Vr_vert_pix(3); + + ar3 = Vr_diag1_pix(1); + br3 = Vr_diag1_pix(2); + cr3 = Vr_diag1_pix(3); + + ar4 = Vr_diag2_pix(1); + br4 = Vr_diag2_pix(2); + cr4 = Vr_diag2_pix(3); + + + % Collect all the constraints: + + A_kk = [al1*al2 bl1*bl2; + al3*al4 bl3*bl4; + ar1*ar2 br1*br2; + ar3*ar4 br3*br4; + al1*ar1 bl1*br1]; + + b_kk = -[cl1*cl2;cl3*cl4;cr1*cr2;cr3*cr4;cl1*cr1]; + + + A = [A;A_kk]; + b = [b;b_kk]; + +end; + +% use all the vanishing points to estimate focal length: + +f_init = sqrt(abs(1./(inv(A'*A)*A'*b))); % if using a two-focal model for initial guess + +%f_init = sqrt(b'*(sum(A')') / (b'*b)) * ones(2,1); % if single focal length model is used + + +% Global calibration matrix (initial guess): + +KK = [f_init(1) 0 c_init(1);0 f_init(2) c_init(2); 0 0 1]; +inv_KK = inv(KK); + + +% Computing of the extrinsic parameters (from the collineations) + +for kk = 1:n_ima, + + eval(['Hlkk = Hl_' num2str(kk) ';']); + + Hl2 = inv_KK*Hlkk; + + sc = mean([norm(Hl2(:,1));norm(Hl2(:,2))]); + + Hl2 = Hl2/sc; + + eval(['Hrkk = Hr_' num2str(kk) ';']); + + Hr2 = inv_KK*Hrkk; + + sc = mean([norm(Hr2(:,1));norm(Hr2(:,2))]); + + Hr2 = Hr2/sc; + + omcl = rodrigues([Hl2(:,1:2) cross(Hl2(:,1),Hl2(:,2))]); + Tcl = Hl2(:,3); + + %omcr = rodrigues([Hr2(:,1:2) cross(Hr2(:,1),Hr2(:,2))]); + %Tcr = Hr2(:,3); + + + omckk = omcl; %rodrigues([H2(:,1:2) cross(H2(:,1),H2(:,2))]); + Tckk = Tcl; %H2(:,3); + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + +end; + + + +% Initialisation of the parameters for global minimization: + +init_param = [f_init;k_init]; + +for kk = 1:n_ima, + eval(['init_param = [init_param; omc_' num2str(kk) '; Tc_' num2str(kk) '];']); +end; + +if ~exist('lsqnonlin'), + + options = [1 1e-4 1e-4 1e-6 0 0 0 0 0 0 0 0 0 6000 0 1e-8 0.1 0]; + + if exist('leastsq'), + sss = ['[param,opt] = leastsq(''multi_error_oulu'',init_param,options,[],n_ima,c_init);']; + else + sss = ['[param,opt] = leastsq2(''multi_error_oulu'',init_param,options,[],n_ima,c_init);']; + end; + +else + + options = optimset('lsqnonlin'); + options.MaxIter = 6000; + options.Display = 'iter'; + sss = ['[param,opt] = lsqnonlin(''multi_error_oulu'',init_param,[],[],options,n_ima,c_init);']; + +end; + + +fprintf(1,'\nOptimization not including the principal point...\n') +eval(sss); + +history = [[init_param;c_init] [param;c_init]]; + +sol_no_center = [param;c_init]; + +init_param = sol_no_center; + +fprintf(1,'\nOptimization including the principal point...\n') + +eval(sss); + +history = [history param]; + + +sol_with_center = param; + + + + +%%% Extraction of the final intrinsic and extrinsic paramaters (in the no-center case): + +solution = sol_no_center; +extract_parameters3D; + +fprintf(1,'\n\nCalibration results without principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,['Pixel error: [click on ''sol. without center'']\n']); + + + + +% Pick the solution with principal point +%%% NOTE: At that point, the user can choose which solution to pick: with or without +%%% principal point estimation. By default, we pick the solution with principal point. + +solution = sol_with_center; + + + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters3D; + + +fprintf(1,'\n\nCalibration results with principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +graphout_calib3D; + + + +fprintf(1,'Note: If the solution is not satisfactory, select solution without center estimation.\n\n'); + + +%%%%%%%%%%%%%% Save all the Calibration results: + +disp('Save calibration results under Calib_Results.mat'); + +string_save = 'save Calib_Results fc kc cc ex x y solution sol_with_center sol_no_center history wintx winty n_ima type_numbering N_slots small_calib_image first_num image_numbers format_image calib_name Hcal Wcal nx ny map dX_default dY_default KK inv_KK dX dY'; + +for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' y_' num2str(kk) ' ex_' num2str(kk) ' omc_' num2str(kk) ' Tc_' num2str(kk) ' Hl_' num2str(kk) ' nl_sq_x_' num2str(kk) ' nl_sq_y_' num2str(kk) ' Hr_' num2str(kk) ' nr_sq_x_' num2str(kk) ' nr_sq_y_' num2str(kk)]; +end; + +eval(string_save); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_cont.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_cont.m new file mode 100755 index 0000000..9ff3f0b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_cont.m @@ -0,0 +1,142 @@ +% Simplified version of go_calib.m + +if ~exist('x_1'), + click_calib; +end; + + +% Initialisation of the parameters for global minimization: + +init_param = [fc;kc]; + +for kk = 1:n_ima, + + if ~exist(['omc_' num2str(kk)]), + eval(['Hkk = H_' num2str(kk) ';']); + H2 = inv_KK*Hkk; + sc = mean([norm(H2(:,1));norm(H2(:,2))]); + H2 = H2/sc; + omckk = rodrigues([H2(:,1:2) cross(H2(:,1),H2(:,2))]); + Tckk = H2(:,3); + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + end; + + eval(['init_param = [init_param; omc_' num2str(kk) '; Tc_' num2str(kk) '];']); +end; + +init_param = [init_param;cc]; + + + +%-------------------- Main Optimization: + +fprintf(1,'\nRe-Optimization...\n') + + +param = init_param; +change = 1; + +iter = 0; + +fprintf(1,'Iteration '); + +while (change > 1e-6)&(iter < 10), + + fprintf(1,'%d...',iter+1); + + JJ = []; + ex = []; + + c = param(6*n_ima + 4 + 3:6*n_ima + 5 + 3); + f = param(1:2); + k = param(3:6); + + for kk = 1:n_ima, + + omckk = param(4+6*(kk-1) + 3:6*kk + 3); + + Tckk = param(6*kk+1 + 3:6*kk+3 + 3); + + eval(['X_kk = X_' num2str(kk) ';']); + eval(['x_kk = x_' num2str(kk) ';']); + + Np = size(X_kk,2); + + JJkk = zeros(2*Np,n_ima * 6 + 8); + + [x,dxdom,dxdT,dxdf,dxdc,dxdk] = project_points(X_kk,omckk,Tckk,f,c,k); + + exkk = x_kk - x; + + ex = [ex;exkk(:)]; + + JJkk(:,1:2) = dxdf; + JJkk(:,3:6) = dxdk; + JJkk(:,4+6*(kk-1) + 3:6*kk + 3) = dxdom; + JJkk(:,6*kk+1 + 3:6*kk+3 + 3) = dxdT; + JJkk(:,6*n_ima + 4 + 3:6*n_ima + 5 + 3) = dxdc; + + JJ = [JJ;JJkk]; + + end; + + param_innov = inv(JJ'*JJ)*(JJ')*ex; + param_up = param + param_innov; + change = norm(param_innov)/norm(param_up); + param = param_up; + iter = iter + 1; + +end; + +fprintf(1,'\n'); + + +sol_with_center = param; + +solution = sol_with_center; + + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters; +comp_error_calib; + +fprintf(1,'\n\nCalibration results with principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +graphout_calib; + + + +fprintf(1,'Note: If the solution is not satisfactory, select solution without center estimation.\n\n'); + + +%%%%%%%%%%%%%% Save all the Calibration results: + +disp('Save calibration results under Calib_Results.mat'); + +string_save = 'save Calib_Results fc kc cc ex x y solution sol_with_center solution_init history wintx winty n_ima type_numbering N_slots small_calib_image first_num image_numbers format_image calib_name Hcal Wcal nx ny map dX_default dY_default KK inv_KK dX dY'; + +for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' y_' num2str(kk) ' ex_' num2str(kk) ' omc_' num2str(kk) ' Rc_' num2str(kk) ' Tc_' num2str(kk) ' H_' num2str(kk) ' Hini_' num2str(kk) ' n_sq_x_' num2str(kk) ' n_sq_y_' num2str(kk) ' wintx_' num2str(kk) ' winty_' num2str(kk) ' dX_' num2str(kk) ' dY_' num2str(kk)]; +end; + +eval(string_save); + +return; + +if exist('calib_data.mat'), + ccc = computer; + if ccc(1)=='P', + eval('!del calib_data.mat'); + else + eval('!rm calib_data.mat'); + end; +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_iter.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_iter.m new file mode 100755 index 0000000..a076214 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/go_calib_optim_iter.m @@ -0,0 +1,332 @@ +%go_calib_optim_iter +% +%Main calibration function. Computes the intrinsic andextrinsic parameters. +%Runs as a script. +% +%INPUT: x_1,x_2,x_3,...: Feature locations on the images +% X_1,X_2,X_3,...: Corresponding grid coordinates +% +%OUTPUT: fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% KK: The camera matrix (containing fc and cc) +% omc_1,omc_2,omc_3,...: 3D rotation vectors attached to the grid positions in space +% Tc_1,Tc_2,Tc_3,...: 3D translation vectors attached to the grid positions in space +% Rc_1,Rc_2,Rc_3,...: 3D rotation matrices corresponding to the omc vectors +% +%Method: Minimizes the pixel reprojection error in the least squares sense over the intrinsic +% camera parameters, and the extrinsic parameters (3D locations of the grids in space) +% +%Note: If the intrinsic camera parameters (fc, cc, kc) do not exist before, they are initialized through +% the function init_intrinsic_param.m. Otherwise, the variables in memory are used as initial guesses. +% +%Note: The row vector active_images consists of zeros and ones. To deactivate an image, set the +% corresponding entry in the active_images vector to zero. +% +%VERY IMPORTANT: This function works for 2D and 3D calibration rigs, except for init_intrinsic_param.m +%that is so far implemented to work only with 2D rigs. +%In the future, a more general function will be there. +%For now, if using a 3D calibration rig, quick_init is set to 1 for an easy initialization of the focal length + + +check_active_images; + + + +quick_init = 0; % Set to 1 for using a quick init (necessary when using 3D rigs) + + + +% Check 3D-ness of the calibration rig: +rig3D = 0; +for kk = ind_active, + eval(['X_kk = X_' num2str(kk) ';']); + if is3D(X_kk), + rig3D = 1; + end; +end; + + + +% If the rig is 3D, then no choice: the only valid initialization is manual! +if rig3D, + quick_init = 1; +end; + + + + +alpha = 0.4; % set alpha = 1; for steepest gradient descent + + +% Conditioning threshold for view rejection +thresh_cond = 1e6; + + + + +%% Initialization of the intrinsic parameters (if necessary) + +if ~exist('cc'), + fprintf(1,'Initialization of the principal point at the center of the image.\n'); + cc = [(nx-1)/2;(ny-1)/2]; +end; + + +if ~exist('kc'), + fprintf(1,'Initialization of the image distortion to zero.\n'); + kc = zeros(4,1); +end; + + +if ~exist('fc')& quick_init, + FOV_angle = 35; % Initial camera field of view in degrees + fprintf(1,['Initialization of the focal length to a FOV of ' num2str(FOV_angle) ' degrees.\n']); + fc = (nx/2)/tan(pi*FOV_angle/360) * ones(2,1); +end; + + +if ~exist('fc'), + % Initialization of the intrinsic parameters: + fprintf(1,'Initialization of the intrinsic parameters using the vanishing points of planar patterns.\n') + init_intrinsic_param; % The right way to go (if quick_init is not active)! +end; + + + +%% Initialization of the extrinsic parameters for global minimization: + +init_param = [fc;kc]; + +for kk = 1:n_ima, + + if exist(['x_' num2str(kk)]), + + eval(['x_kk = x_' num2str(kk) ';']); + eval(['X_kk = X_' num2str(kk) ';']); + + if (isnan(x_kk(1,1))), + if active_images(kk), + fprintf(1,'Warning: Cannot calibrate with image %d. Need to extract grid corners first.\n',kk) + fprintf(1,' Set active_images(%d)=1; and run Extract grid corners.\n',kk) + end; + active_images(kk) = 0; + end; + if active_images(kk), + [omckk,Tckk] = compute_extrinsic_init(x_kk,X_kk,fc,cc,kc); + [omckk,Tckk,Rckk,JJ_kk] = compute_extrinsic_refine(omckk,Tckk,x_kk,X_kk,fc,cc,kc,20,thresh_cond); + if cond(JJ_kk)> thresh_cond, + active_images(kk) = 0; + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + fprintf(1,'\nWarning: View #%d ill-conditioned. This image is now set inactive.\n',kk) + desactivated_images = [desactivated_images kk]; + end; + if isnan(omckk(1,1)), + %fprintf(1,'\nWarning: Desactivating image %d. Re-activate it later by typing:\nactive_images(%d)=1;\nand re-run optimization\n',[kk kk]) + active_images(kk) = 0; + end; + else + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + end; + + else + + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + + if active_images(kk), + fprintf(1,'Warning: Cannot calibrate with image %d. Need to extract grid corners first.\n',kk) + fprintf(1,' Set active_images(%d)=1; and run Extract grid corners.\n',kk) + end; + + active_images(kk) = 0; + + end; + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + + eval(['init_param = [init_param; omc_' num2str(kk) '; Tc_' num2str(kk) '];']); + +end; + + +check_active_images; + +init_param = [init_param;cc]; + +%-------------------- Main Optimization: + +fprintf(1,'\nMain calibration optimization procedure - Number of images: %d\n',length(ind_active)); + + +% The following vector helps to select the variables to update: +ind_Jac = find([ones(6,1);reshape(ones(6,1)*active_images,6*n_ima,1);ones(2,1)])'; + +param = init_param; +change = 1; + +iter = 0; + +fprintf(1,'Gradient descent iterations: '); + +param_list = param; + +MaxIter = 30; + + +while (change > 1e-6)&(iter < MaxIter), + + fprintf(1,'%d...',iter+1); + + + %% The first step consists of updating the whole vector of knowns (intrinsic + extrinsic of active + %% images) through a one step steepest gradient descent. + + JJ = []; + ex = []; + + c = param(6*n_ima + 4 + 3:6*n_ima + 5 + 3); + f = param(1:2); + k = param(3:6); + + for kk = 1:n_ima, + + if active_images(kk), + + omckk = param(4+6*(kk-1) + 3:6*kk + 3); + + Tckk = param(6*kk+1 + 3:6*kk+3 + 3); + + if isnan(omckk(1)), + fprintf(1,'Intrinsic parameters at frame %d do not exist\n',kk); + return; + end; + + eval(['X_kk = X_' num2str(kk) ';']); + eval(['x_kk = x_' num2str(kk) ';']); + + Np = size(X_kk,2); + + JJkk = zeros(2*Np,n_ima * 6 + 8); + + [x,dxdom,dxdT,dxdf,dxdc,dxdk] = project_points(X_kk,omckk,Tckk,f,c,k); + + exkk = x_kk - x; + + ex = [ex;exkk(:)]; + + JJkk(:,1:2) = dxdf; + JJkk(:,3:6) = dxdk; + JJkk(:,4+6*(kk-1) + 3:6*kk + 3) = dxdom; + JJkk(:,6*kk+1 + 3:6*kk+3 + 3) = dxdT; + JJkk(:,6*n_ima + 4 + 3:6*n_ima + 5 + 3) = dxdc; + + JJ = [JJ;JJkk]; + + + % Check if this view is ill-conditioned: + JJ_kk = [dxdom dxdT]; + if cond(JJ_kk)> thresh_cond, + active_images(kk) = 0; + fprintf(1,'\nWarning: View #%d ill-conditioned. This image is now set inactive.\n',kk) + desactivated_images = [desactivated_images kk]; + param(4+6*(kk-1) + 3:6*kk+3 + 3) = NaN*ones(6,1); + end; + + + end; + + end; + + + % List of active images (necessary if changed): + check_active_images; + ind_Jac = find([ones(6,1);reshape(ones(6,1)*active_images,6*n_ima,1);ones(2,1)])'; + + + JJ = JJ(:,ind_Jac); + + JJ2 = JJ'*JJ; + + + % Smoothing coefficient: + + alpha2 = 1-(1-alpha)^(iter+1); %set to 1 to undo any smoothing! + + + param_innov = alpha2*inv(JJ2)*(JJ')*ex; + param_up = param(ind_Jac) + param_innov; + param(ind_Jac) = param_up; + + + % New intrinsic parameters: + + fc_current = param(1:2); + cc_current = param(6*n_ima + 4 + 3:6*n_ima + 5 + 3); + kc_current = param(3:6); + + + % Change on the intrinsic parameters: + change = norm([fc_current;cc_current] - [f;c])/norm([fc_current;cc_current]); + + + %% Second step: (optional) - It makes convergence faster, and the region of convergence LARGER!!! + %% Recompute the extrinsic parameters only using compute_extrinsic.m (this may be useful sometimes) + %% The complete gradient descent method is useful to precisely update the intrinsic parameters. + + MaxIter2 = 20; + + for kk = 1:n_ima, + if active_images(kk), + omc_current = param(4+6*(kk-1) + 3:6*kk + 3); + Tc_current = param(6*kk+1 + 3:6*kk+3 + 3); + eval(['X_kk = X_' num2str(kk) ';']); + eval(['x_kk = x_' num2str(kk) ';']); + [omc_current,Tc_current] = compute_extrinsic_init(x_kk,X_kk,fc_current,cc_current,kc_current); + [omckk,Tckk,Rckk,JJ_kk] = compute_extrinsic_refine(omc_current,Tc_current,x_kk,X_kk,fc_current,cc_current,kc_current,MaxIter2,thresh_cond); + if cond(JJ_kk)> thresh_cond, + active_images(kk) = 0; + fprintf(1,'\nWarning: View #%d ill-conditioned. This image is now set inactive.\n',kk) + desactivated_images = [desactivated_images kk]; + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + end; + param(4+6*(kk-1) + 3:6*kk + 3) = omckk; + param(6*kk+1 + 3:6*kk+3 + 3) = Tckk; + end; + end; + + %fprintf(1,'\n\nCalibration results after optimization:\n\n'); + %fprintf(1,'focal = [%3.5f %3.5f]\n',fc_current); + %fprintf(1,'center = [%3.5f %3.5f]\n',cc_current); + %fprintf(1,'distortion = [%3.5f %3.5f %3.5f %3.5f]\n\n',kc_current); + + + param_list = [param_list param]; + + iter = iter + 1; + +end; + +fprintf(1,'\n'); + + +sol_with_center = param; + +solution = param; + + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters; +comp_error_calib; + +fprintf(1,'\n\nCalibration results after optimization:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib.m new file mode 100755 index 0000000..a3f7040 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib.m @@ -0,0 +1,12 @@ + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%% SHOW EXTRINSIC RESULTS %%%%%%%%%%%%%%%%%%%%%%%% + +ext_calib; + +%%%%%%%%%%%%%%%%%%%% REPROJECT ON THE IMAGES %%%%%%%%%%%%%%%%%%%%%%%% + +reproject_calib; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib3D.m new file mode 100755 index 0000000..b7edf43 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/graphout_calib3D.m @@ -0,0 +1,153 @@ + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + + +% Color code for each image: + +colors = 'brgkcm'; + + +%%% Show the extrinsic parameters + +IP = 8*dX*([0 nx-1 nx-1 0 0 ; 0 0 ny-1 ny-1 0;1 1 1 1 1] - [cc;0]*ones(1,5)) ./ ([fc;1]*ones(1,5)); + + +figure(4); +[a,b] = view; + +figure(4); +plot3(5*[0 dX 0 0 0 0 ],5*[0 0 0 0 0 dX],-5*[0 0 0 dX 0 0 ],'b-','linewidth',2'); +hold on; +plot3(IP(1,:),IP(3,:),-IP(2,:),'r-','linewidth',2); +text(6*dX,0,0,'X_c'); +text(-dX,5*dX,0,'Z_c'); +text(0,0,-6*dX,'Y_c'); +text(-dX,-dX,dX,'O_c'); + + +for kk = 1:n_ima, + + eval(['XX_kk = X_' num2str(kk) ';']); + eval(['omc_kk = omc_' num2str(kk) ';']); + eval(['Tc_kk = Tc_' num2str(kk) ';']); + + eval(['nl_sq_x = nl_sq_x_' num2str(kk) ';']); + eval(['nl_sq_y = nl_sq_y_' num2str(kk) ';']); + + eval(['nr_sq_x = nr_sq_x_' num2str(kk) ';']); + eval(['nr_sq_y = nr_sq_y_' num2str(kk) ';']); + + R_kk = rodrigues(omc_kk); + + YY_kk = R_kk * XX_kk + Tc_kk * ones(1,length(XX_kk)); + + YYl_kk = YY_kk(:,1:(nl_sq_x+1)*(nl_sq_y+1)); + YYr_kk = YY_kk(:,(nl_sq_x+1)*(nl_sq_y+1)+1:end); + + + eval(['YYl_' num2str(kk) ' = YYl_kk;']); + eval(['YYr_' num2str(kk) ' = YYr_kk;']); + + uu = [-dX;-dY;0]/2; + uu = R_kk * uu + Tc_kk; + + YYlx = zeros(nl_sq_x+1,nl_sq_y+1); + YYly = zeros(nl_sq_x+1,nl_sq_y+1); + YYlz = zeros(nl_sq_x+1,nl_sq_y+1); + + YYrx = zeros(nr_sq_x+1,nr_sq_y+1); + YYry = zeros(nr_sq_x+1,nr_sq_y+1); + YYrz = zeros(nr_sq_x+1,nr_sq_y+1); + + YYlx(:) = YYl_kk(1,:); + YYly(:) = YYl_kk(2,:); + YYlz(:) = YYl_kk(3,:); + + YYrx(:) = YYr_kk(1,:); + YYry(:) = YYr_kk(2,:); + YYrz(:) = YYr_kk(3,:); + + + %keyboard; + + figure(4); + hhh= mesh(YYlx,YYlz,-YYly); + set(hhh,'edgecolor',colors(rem(kk-1,6)+1),'linewidth',1); %,'facecolor','none'); + %plot3(YY_kk(1,:),YY_kk(3,:),-YY_kk(2,:),['o' colors(rem(kk-1,6)+1)]); + hhh= mesh(YYrx,YYrz,-YYry); + set(hhh,'edgecolor',colors(rem(kk-1,6)+1),'linewidth',1); + text(uu(1),uu(3),-uu(2),num2str(kk),'fontsize',14,'color',colors(rem(kk-1,6)+1)); + +end; + +figure(4);rotate3d on; +axis('equal'); +title('Extrinsic parameters'); +%view(60,30); +view(a,b); +hold off; + + + +% Reproject the patterns on the images, and compute the pixel errors: + +% Reload the images if necessary + +if ~exist('I_1'), + ima_read_calib; + if no_image_file, + return; + end; +end; + + +ex = []; % Global error vector +x = []; % Detected corners on the image plane +y = []; % Reprojected points + +for kk = 1:n_ima, + + eval(['omckk = omc_' num2str(kk) ';']); + eval(['Tckk = Tc_' num2str(kk) ';']); + + Rkk = rodrigues(omckk); + + eval(['y_' num2str(kk) ' = project2_oulu(X_' num2str(kk) ',Rkk,Tckk,fc,cc,kc);']); + + eval(['ex_' num2str(kk) ' = x_' num2str(kk) ' -y_' num2str(kk) ';']); + + eval(['x_kk = x_' num2str(kk) ';']); + + figure(4+kk); + eval(['I = I_' num2str(kk) ';']); + image(I); hold on; + colormap(gray(256)); + title(['Image ' num2str(kk) ' - Image points (+) and reprojected grid points (o)']); + eval(['plot(x_' num2str(kk) '(1,:)+1,x_' num2str(kk) '(2,:)+1,''r+'');']); + eval(['plot(y_' num2str(kk) '(1,:)+1,y_' num2str(kk) '(2,:)+1,''' colors(rem(kk-1,6)+1) 'o'');']); + zoom on; + hold off; + + + eval(['ex = [ex ex_' num2str(kk) '];']); + eval(['x = [x x_' num2str(kk) '];']); + eval(['y = [y y_' num2str(kk) '];']); + +end; + + +figure(5+n_ima); +for kk = 1:n_ima, + eval(['plot(ex_' num2str(kk) '(1,:),ex_' num2str(kk) '(2,:),''' colors(rem(kk-1,6)+1) '+'');']); + hold on; +end; +hold off; +axis('equal'); +title('Reprojection error (in pixel)'); +xlabel('x'); +ylabel('y'); + +err_std = std(ex')'; + +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ima_read_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ima_read_calib.m new file mode 100755 index 0000000..09cef59 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/ima_read_calib.m @@ -0,0 +1,107 @@ + +if ~exist('calib_name'), + data_calib; +end; + +check_active_images; + +images_read = active_images; + +image_numbers = first_num:n_ima-1+first_num; + +no_image_file = 0; + +i = 1; + +while (i <= n_ima), % & (~no_image_file), + + if active_images(i), + + %fprintf(1,'Loading image %d...\n',i); + + if ~type_numbering, + number_ext = num2str(image_numbers(i)); + else + number_ext = sprintf(['%.' num2str(N_slots) 'd'],image_numbers(i)); + end; + + ima_name = [calib_name number_ext '.' format_image] + + if i == ind_active(1), + fprintf(1,'Loading image '); + end; + + if exist(ima_name), + + fprintf(1,'%d...',i); + + if format_image(1) == 'p', + Ii = double(pgmread(ima_name)); + else + if format_image(1) == 'r', + Ii = readras(ima_name); + else + Ii = double(imread(ima_name)); + end; + end; + + if size(Ii,3)>1, + Ii = Ii(:,:,2); + end; + + eval(['I_' num2str(i) ' = Ii;']); + + else + + fprintf(1,'%d...',i); + + images_read(i) = 0; + + no_image_file = 1; + + end; + + end; + + i = i+1; + +end; + + +if no_image_file, + + fprintf(1,'\nWARNING! Cannot load calibration images\n'); + +else + + fprintf(1,'\n'); + + if size(I_1,1)~=480, + small_calib_image = 1; + else + small_calib_image = 0; + end; + + [Hcal,Wcal] = size(I_1); % size of the calibration image + + [ny,nx] = size(I_1); + + clickname = []; + + map = gray(256); + + %string_save = 'save calib_data n_ima type_numbering N_slots image_numbers format_image calib_name Hcal Wcal nx ny map small_calib_image'; + + %eval(string_save); + + disp('done'); + %click_calib; + +end; + +if ~exist('map'), map = gray(256); end; + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_calib_param.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_calib_param.m new file mode 100755 index 0000000..92e14be --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_calib_param.m @@ -0,0 +1,210 @@ +%init_calib_param +% +%Initialization of the intrinsic and extrinsic parameters. +%Runs as a script. +% +%This function is obsolete with init_intrinsic_param called from go_calib_optim +% +%INPUT: x_1,x_2,x_3,...: Feature locations on the images +% X_1,X_2,X_3,...: Corresponding grid coordinates +% +%OUTPUT: fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% KK: The camera matrix (containing fc and cc) +% omc_1,omc_2,omc_3,...: 3D rotation vectors attached to the grid positions in space +% Tc_1,Tc_2,Tc_3,...: 3D translation vectors attached to the grid positions in space +% Rc_1,Rc_2,Rc_3,...: 3D rotation matrices corresponding to the omc vectors +% +%Method: Compute the planar homographies H_1, H_2, H_3, ... and computes +% the focal length fc from orthogonal vanishing points constraint. +% The principal point cc is assumed at the center of the image. +% Assumes no image distortion (kc = [0;0;0;0]) +% Once the intrinsic parameters are estimated, the extrinsic parameters +% are computed for each image. +% +%Note: The row vector active_images consists of zeros and ones. To deactivate an image, set the +% corresponding entry in the active_images vector to zero. +% +% +%Important functions called within that program: +% +%compute_homography.m: Computes the planar homography between points on the grid in 3D, and the image plane. +% +%compute_extrinsic.m: Computes the location of a grid assuming known intrinsic parameters. +% This function is called at the initialization step. + + + + +check_active_images; + +if ~exist(['x_' num2str(ind_active(1)) ]), + click_calib; +end; + + +fprintf(1,'\nInitialization of the calibration parameters - Number of images: %d\n',length(ind_active)); + + +% Initialize the homographies: + +for kk = 1:n_ima, + eval(['x_kk = x_' num2str(kk) ';']); + eval(['X_kk = X_' num2str(kk) ';']); + if (isnan(x_kk(1,1))), + if active_images(kk), + fprintf(1,'WARNING: Cannot calibrate with image %d. Need to extract grid corners first.\n',kk) + fprintf(1,' Set active_images(%d)=1; and run Extract grid corners.\n',kk) + end; + active_images(kk) = 0; + end; + if active_images(kk), + eval(['H_' num2str(kk) ' = compute_homography(x_kk,X_kk(1:2,:));']); + else + eval(['H_' num2str(kk) ' = NaN*ones(3,3);']); + end; +end; + +check_active_images; + +% initial guess for principal point and distortion: + +if ~exist('nx'), [ny,nx] = size(I); end; + +c_init = [nx;ny]/2 - 0.5; % initialize at the center of the image +k_init = [0;0;0;0]; % initialize to zero (no distortion) + + + +% Compute explicitely the focal length using all the (mutually orthogonal) vanishing points +% note: The vanihing points are hidden in the planar collineations H_kk + +A = []; +b = []; + +% matrix that subtract the principal point: +Sub_cc = [1 0 -c_init(1);0 1 -c_init(2);0 0 1]; + +for kk=1:n_ima, + + if active_images(kk), + + eval(['Hkk = H_' num2str(kk) ';']); + + Hkk = Sub_cc * Hkk; + + % Extract vanishing points (direct and diagonals): + + V_hori_pix = Hkk(:,1); + V_vert_pix = Hkk(:,2); + V_diag1_pix = (Hkk(:,1)+Hkk(:,2))/2; + V_diag2_pix = (Hkk(:,1)-Hkk(:,2))/2; + + V_hori_pix = V_hori_pix/norm(V_hori_pix); + V_vert_pix = V_vert_pix/norm(V_vert_pix); + V_diag1_pix = V_diag1_pix/norm(V_diag1_pix); + V_diag2_pix = V_diag2_pix/norm(V_diag2_pix); + + a1 = V_hori_pix(1); + b1 = V_hori_pix(2); + c1 = V_hori_pix(3); + + a2 = V_vert_pix(1); + b2 = V_vert_pix(2); + c2 = V_vert_pix(3); + + a3 = V_diag1_pix(1); + b3 = V_diag1_pix(2); + c3 = V_diag1_pix(3); + + a4 = V_diag2_pix(1); + b4 = V_diag2_pix(2); + c4 = V_diag2_pix(3); + + A_kk = [a1*a2 b1*b2; + a3*a4 b3*b4]; + + b_kk = -[c1*c2;c3*c4]; + + + A = [A;A_kk]; + b = [b;b_kk]; + + end; + +end; + + +% use all the vanishing points to estimate focal length: + +f_init = sqrt(abs(1./(inv(A'*A)*A'*b))); % if using a two-focal model for initial guess + +%f_init = sqrt(b'*(sum(A')') / (b'*b)) * ones(2,1); % if single focal length model is used + + +% Global calibration matrix (initial guess): + +KK = [f_init(1) 0 c_init(1);0 f_init(2) c_init(2); 0 0 1]; +inv_KK = inv(KK); + + +cc = c_init; +fc = f_init; +kc = k_init; + + +% Computing of the extrinsic parameters (from the collineations) + +for kk = 1:n_ima, + + if active_images(kk), + + + eval(['x_kk = x_' num2str(kk) ';']); + eval(['X_kk = X_' num2str(kk) ';']); + + [omckk,Tckk] = compute_extrinsic(x_kk,X_kk,fc,cc,kc); + + Rckk = rodrigues(omc_kk); + + else + + omckk = NaN*ones(3,1); + Tckk = NaN*ones(3,1); + Rckk = NaN*ones(3,3); + + end; + + eval(['omc_' num2str(kk) ' = omckk;']); + eval(['Rc_' num2str(kk) ' = Rckk;']); + eval(['Tc_' num2str(kk) ' = Tckk;']); + +end; + + +% Initialization of the parameters for global minimization: + +init_param = [f_init;k_init]; + +for kk = 1:n_ima, + eval(['init_param = [init_param; omc_' num2str(kk) '; Tc_' num2str(kk) '];']); +end; + +solution_init = [init_param;c_init]; + +solution = solution_init; + +comp_error_calib; + +fprintf(1,'\n\nCalibration parameters after initialization:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +%graphout_calib; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_intrinsic_param.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_intrinsic_param.m new file mode 100755 index 0000000..eac21ba --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/init_intrinsic_param.m @@ -0,0 +1,153 @@ +%init_intrinsic_param +% +%Initialization of the intrinsic parameters. +%Runs as a script. +% +%INPUT: x_1,x_2,x_3,...: Feature locations on the images +% X_1,X_2,X_3,...: Corresponding grid coordinates +% +%OUTPUT: fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% KK: The camera matrix (containing fc and cc) +% +%Method: Computes the planar homographies H_1, H_2, H_3, ... and computes +% the focal length fc from orthogonal vanishing points constraint. +% The principal point cc is assumed at the center of the image. +% Assumes no image distortion (kc = [0;0;0;0]) +% +%Note: The row vector active_images consists of zeros and ones. To deactivate an image, set the +% corresponding entry in the active_images vector to zero. +% +% +%Important function called within that program: +% +%compute_homography.m: Computes the planar homography between points on the grid in 3D, and the image plane. +% +% +%VERY IMPORTANT: This function works onyl with 2D rigs. +%In the future, a more general function will be there (working with 3D rigs as well). + + + +check_active_images; + +if ~exist(['x_' num2str(ind_active(1)) ]), + click_calib; +end; + + +fprintf(1,'\nInitialization of the intrinsic parameters - Number of images: %d\n',length(ind_active)); + + +% Initialize the homographies: + +for kk = 1:n_ima, + eval(['x_kk = x_' num2str(kk) ';']); + eval(['X_kk = X_' num2str(kk) ';']); + if (isnan(x_kk(1,1))), + if active_images(kk), + fprintf(1,'WARNING: Cannot calibrate with image %d. Need to extract grid corners first.\n',kk) + fprintf(1,' Set active_images(%d)=1; and run Extract grid corners.\n',kk) + end; + active_images(kk) = 0; + end; + if active_images(kk), + eval(['H_' num2str(kk) ' = compute_homography(x_kk,X_kk(1:2,:));']); + else + eval(['H_' num2str(kk) ' = NaN*ones(3,3);']); + end; +end; + +check_active_images; + +% initial guess for principal point and distortion: + +if ~exist('nx'), [ny,nx] = size(I); end; + +c_init = [nx;ny]/2 - 0.5; % initialize at the center of the image +k_init = [0;0;0;0]; % initialize to zero (no distortion) + + + +% Compute explicitely the focal length using all the (mutually orthogonal) vanishing points +% note: The vanihing points are hidden in the planar collineations H_kk + +A = []; +b = []; + +% matrix that subtract the principal point: +Sub_cc = [1 0 -c_init(1);0 1 -c_init(2);0 0 1]; + +for kk=1:n_ima, + + if active_images(kk), + + eval(['Hkk = H_' num2str(kk) ';']); + + Hkk = Sub_cc * Hkk; + + % Extract vanishing points (direct and diagonals): + + V_hori_pix = Hkk(:,1); + V_vert_pix = Hkk(:,2); + V_diag1_pix = (Hkk(:,1)+Hkk(:,2))/2; + V_diag2_pix = (Hkk(:,1)-Hkk(:,2))/2; + + V_hori_pix = V_hori_pix/norm(V_hori_pix); + V_vert_pix = V_vert_pix/norm(V_vert_pix); + V_diag1_pix = V_diag1_pix/norm(V_diag1_pix); + V_diag2_pix = V_diag2_pix/norm(V_diag2_pix); + + a1 = V_hori_pix(1); + b1 = V_hori_pix(2); + c1 = V_hori_pix(3); + + a2 = V_vert_pix(1); + b2 = V_vert_pix(2); + c2 = V_vert_pix(3); + + a3 = V_diag1_pix(1); + b3 = V_diag1_pix(2); + c3 = V_diag1_pix(3); + + a4 = V_diag2_pix(1); + b4 = V_diag2_pix(2); + c4 = V_diag2_pix(3); + + A_kk = [a1*a2 b1*b2; + a3*a4 b3*b4]; + + b_kk = -[c1*c2;c3*c4]; + + + A = [A;A_kk]; + b = [b;b_kk]; + + end; + +end; + + +% use all the vanishing points to estimate focal length: + +f_init = sqrt(abs(1./(inv(A'*A)*A'*b))); % if using a two-focal model for initial guess + +%f_init = sqrt(b'*(sum(A')') / (b'*b)) * ones(2,1); % if single focal length model is used + + +% Global calibration matrix (initial guess): + +KK = [f_init(1) 0 c_init(1);0 f_init(2) c_init(2); 0 0 1]; +inv_KK = inv(KK); + + +cc = c_init; +fc = f_init; +kc = k_init; + + +fprintf(1,'\n\nCalibration parameters after initialization:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n\n',kc); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/is3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/is3D.m new file mode 100755 index 0000000..ab00b3d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/is3D.m @@ -0,0 +1,19 @@ +function test = is3D(X), + + +Np = size(X,2); + +%% Check for planarity of the structure: + +X_mean = mean(X')'; + +Y = X - (X_mean*ones(1,Np)); + +YY = Y*Y'; + +[U,S,V] = svd(YY); + +r = S(3,3)/S(2,2); + +test = (r > 1e-3); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loading_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loading_calib.m new file mode 100755 index 0000000..a0f50d2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loading_calib.m @@ -0,0 +1,10 @@ +if ~exist('Calib_Results.mat'), + fprintf(1,'\nCalibration file Calib_Results.mat not found!\n'); + return; +end; + +fprintf(1,'\nLoading calibration results from Calib_Results.mat\n'); + +load Calib_Results + +fprintf(1,'done\n'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadinr.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadinr.m new file mode 100755 index 0000000..91b6f89 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadinr.m @@ -0,0 +1,52 @@ +%LOADINR Load an INRIMAGE format file +% +% LOADINR(filename, im) +% +% Load an INRIA image format file and return it as a matrix +% +% SEE ALSO: saveinr +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1996 + +function im = loadinr(fname, im) + + fid = fopen(fname, 'r'); + + s = fgets(fid); + if strcmp(s(1:12), '#INRIMAGE-4#') == 0, + error('not INRIMAGE format'); + end + + % not very complete, only looks for the X/YDIM keys + while 1, + s = fgets(fid); + n = length(s) - 1; + if s(1) == '#', + break + end + if strcmp(s(1:5), 'XDIM='), + cols = str2num(s(6:n)); + end + if strcmp(s(1:5), 'YDIM='), + rows = str2num(s(6:n)); + end + if strcmp(s(1:4), 'CPU='), + if strcmp(s(5:n), 'sun') == 0, + error('not sun data ordering'); + end + end + + end + disp(['INRIMAGE format file ' num2str(rows) ' x ' num2str(cols)]) + + % now the binary data + fseek(fid, 256, 'bof'); + [im count] = fread(fid, [cols rows], 'float32'); + im = im'; + if count ~= (rows*cols), + error('file too short'); + end + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadpgm.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadpgm.m new file mode 100755 index 0000000..9386111 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadpgm.m @@ -0,0 +1,89 @@ +%LOADPGM Load a PGM image +% +% I = loadpgm(filename) +% +% Returns a matrix containing the image loaded from the PGM format +% file filename. Handles ASCII (P2) and binary (P5) PGM file formats. +% +% If the filename has no extension, and open fails, a '.pgm' will +% be appended. +% +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function I = loadpgm(file) + white = [' ' 9 10 13]; % space, tab, lf, cr + white = setstr(white); + + fid = fopen(file, 'r'); + if fid < 0, + fid = fopen([file '.pgm'], 'r'); + end + if fid < 0, + error('Couldn''t open file'); + end + + magic = fread(fid, 2, 'char'); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + cols = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + rows = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + maxval = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + if magic(1) == 'P', + if magic(2) == '2', + disp(['ASCII PGM file ' num2str(rows) ' x ' num2str(cols)]) + I = fscanf(fid, '%d', [cols rows])'; + elseif magic(2) == '5', + disp(['Binary PGM file ' num2str(rows) ' x ' num2str(cols)]) + if maxval == 1, + fmt = 'unint1'; + elseif maxval == 15, + fmt = 'uint4'; + elseif maxval == 255, + fmt = 'uint8'; + elseif maxval == 2^32-1, + fmt = 'uint32'; + end + I = fread(fid, [cols rows], fmt)'; + else + disp('Not a PGM file'); + end + end + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadppm.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadppm.m new file mode 100755 index 0000000..6dd7ca4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/loadppm.m @@ -0,0 +1,101 @@ +%LOADPPM Load a PPM image +% +% [R,G,B] = loadppm(filename) +% +% Returns a matrix containing the image loaded from the PPM format +% file filename. Handles ASCII (P3) and binary (P6) PPM file formats. +% +% If the filename has no extension, and open fails, a '.ppm' and +% '.pnm' extension will be tried. +% +% SEE ALSO: saveppm loadpgm +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function [R,G,B] = loadppm(file) + white = [' ' 9 10 13]; % space, tab, lf, cr + white = setstr(white); + + fid = fopen(file, 'r'); + if fid < 0, + fid = fopen([file '.ppm'], 'r'); + end + if fid < 0, + fid = fopen([file '.pnm'], 'r'); + end + if fid < 0, + error('Couldn''t open file'); + end + + magic = fread(fid, 2, 'char'); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + cols = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + rows = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + maxval = fscanf(fid, '%d', 1); + while 1 + c = fread(fid,1,'char'); + if c == '#', + fgetl(fid); + elseif ~any(c == white) + fseek(fid, -1, 'cof'); % unputc() + break; + end + end + if magic(1) == 'P', + if magic(2) == '3', + disp(['ASCII PPM file ' num2str(rows) ' x ' num2str(cols)]) + I = fscanf(fid, '%d', [cols*3 rows]); + elseif magic(2) == '6', + disp(['Binary PPM file ' num2str(rows) ' x ' num2str(cols)]) + if maxval == 1, + fmt = 'unint1'; + elseif maxval == 15, + fmt = 'uint4'; + elseif maxval == 255, + fmt = 'uint8'; + elseif maxval == 2^32-1, + fmt = 'uint32'; + end + I = fread(fid, [cols*3 rows], fmt); + else + disp('Not a PPM file'); + end + end + % + % now the matrix has interleaved columns of R, G, B + % + I = I'; + size(I) + R = I(:,1:3:(cols*3)); + G = I(:,2:3:(cols*3)); + B = I(:,3:3:(cols*3)); + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/mean_std_robust.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/mean_std_robust.m new file mode 100755 index 0000000..0d18a62 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/mean_std_robust.m @@ -0,0 +1,7 @@ +function [m,s] = mean_std_robust(x); + +x = x(:); + +m = median(x); + +s = median(abs(x - m))*1.4836; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/multi_error_oulu.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/multi_error_oulu.m new file mode 100755 index 0000000..8657158 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/multi_error_oulu.m @@ -0,0 +1,49 @@ +function ex = multi_error_oulu(param,n_ima,cc); + +global X_1 x_1 X_2 x_2 X_3 x_3 X_4 x_4 X_5 x_5 X_6 x_6 X_7 x_7 X_8 x_8 X_9 x_9 X_10 x_10 X_11 x_11 X_12 x_12 X_13 x_13 X_14 x_14 X_15 x_15 X_16 x_16 X_17 x_17 X_18 x_18 X_19 x_19 X_20 x_20 X_21 x_21 X_22 x_22 X_23 x_23 X_24 x_24 X_25 x_25 X_26 x_26 X_27 x_27 X_28 x_28 X_29 x_29 X_30 x_30 + +fc = param(1:2); +kc = param(3:6); +%ppc = param(5:6); + +if length(param) > 6*n_ima + 3 + 3, + + cc = param(6*n_ima + 4 + 3:6*n_ima + 5 + 3); + + if length(param) > 6*n_ima + 5 + 3, + + c_d = param(6*n_ima + 6 + 3 :6*n_ima + 7 + 3); + + else + + c_d = [0;0]; + + end; + +else + + c_d = [0;0]; + +end; + + + +ex = []; + +%keyboard; + +for kk = 1:n_ima, + + omckk = param(4+6*(kk-1) + 3:6*kk + 3); + + Tckk = param(6*kk+1 + 3:6*kk+3 + 3); + + Rkk = rodrigues(omckk); + + eval(['ykk = project2_oulu(X_' num2str(kk) ',Rkk,Tckk,fc,cc,kc);']); + + eval(['exkk = x_' num2str(kk) ' -ykk;']); + + ex = [ex;exkk(:)]; + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/normalize.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/normalize.m new file mode 100755 index 0000000..0a37378 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/normalize.m @@ -0,0 +1,32 @@ +function [xn] = normalize(x_kk,fc,cc,kc), + +%normalize +% +%[xn] = normalize(x_kk,fc,cc,kc) +% +%Computes the normalized coordinates xn given the pixel coordinates x_kk +%and the intrinsic camera parameters fc, cc and kc. +% +%INPUT: x_kk: Feature locations on the images +% fc: Camera focal length +% cc: Principal point coordinates +% kc: Distortion coefficients +% +%OUTPUT: xn: Normalized feature locations on the image plane (a 2XN matrix) +% +%Important functions called within that program: +% +%comp_distortion_oulu: undistort pixel coordinates. + + + +% First subtract principal point, and divide by the focal length: + +x_distort = [(x_kk(1,:) - cc(1))/fc(1);(x_kk(2,:) - cc(2))/fc(2)]; + + +%Compensate for lens distortion: + +xn = comp_distortion_oulu(x_distort,kc); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/pgmread.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/pgmread.m new file mode 100755 index 0000000..c96ccb7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/pgmread.m @@ -0,0 +1,26 @@ +function img = pgmread(filename) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + +fid = fopen(filename,'r'); +fscanf(fid, 'P5\n'); +cmt = '#'; +while findstr(cmt, '#'), + cmt = fgets(fid); + if length(findstr(cmt, '#')) ~= 1, + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end +end + +%fgets(fid); + +%img = fscanf(fid,'%d',size); +%img = img'; + +img = fread(fid,[y,x],'uint8'); +img = img'; +fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project2_oulu.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project2_oulu.m new file mode 100755 index 0000000..c5c4a34 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project2_oulu.m @@ -0,0 +1,53 @@ +function [x] = project2_oulu(X,R,T,f,t,k) +%PROJECT Subsidiary to calib + +% (c) Pietro Perona -- March 24, 1994 +% California Institute of Technology +% Pasadena, CA +% +% Renamed because project exists in matlab 5.2!!! +% Now uses the more elaborate intrinsic model from Oulu + + + +[m,n] = size(X); + +Y = R*X + T*ones(1,n); +Z = Y(3,:); + +f = f(:); %% make a column vector +if length(f)==1, + f = [f f]'; +end; + +x = (Y(1:2,:) ./ (ones(2,1) * Z)) ; + + +radius_2 = x(1,:).^2 + x(2,:).^2; + +if length(k) > 1, + + radial_distortion = 1 + ones(2,1) * ((k(1) * radius_2) + (k(2) * radius_2.^2)); + + if length(k) < 4, + + delta_x = zeros(2,n); + + else + + delta_x = [2*k(3)*x(1,:).*x(2,:) + k(4)*(radius_2 + 2*x(1,:).^2) ; + k(3) * (radius_2 + 2*x(2,:).^2)+2*k(4)*x(1,:).*x(2,:)]; + + end; + + +else + + radial_distortion = 1 + ones(2,1) * ((k(1) * radius_2)); + + delta_x = zeros(2,n); + +end; + + +x = (x .* radial_distortion + delta_x).* (f * ones(1,n)) + t*ones(1,n); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project_points.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project_points.m new file mode 100755 index 0000000..1823490 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/project_points.m @@ -0,0 +1,276 @@ +function [xp,dxpdom,dxpdT,dxpdf,dxpdc,dxpdk] = project_points(X,om,T,f,c,k) + +%project_points.m +% +%[xp,dxpdom,dxpdT,dxpdf,dxpdc,dxpdk] = project_points(X,om,T,f,c,k) +% +%Projects a 3D structure onto the image plane. +% +%INPUT: X: 3D structure in the world coordinate frame (3xN matrix for N points) +% (om,T): Rigid motion parameters between world coordinate frame and camera reference frame +% om: rotation vector (3x1 vector); T: translation vector (3x1 vector) +% f: camera focal length in units of horizontal and vertical pixel units (2x1 vector) +% c: principal point location in pixel units (2x1 vector) +% k: Distortion coefficients (radial and tangential) (4x1 vector) +% +%OUTPUT: xp: Projected pixel coordinates (2xN matrix for N points) +% dxpdom: Derivative of xp with respect to om ((2N)x3 matrix) +% dxpdT: Derivative of xp with respect to T ((2N)x3 matrix) +% dxpdf: Derivative of xp with respect to f ((2N)x2 matrix) +% dxpdc: Derivative of xp with respect to c ((2N)x2 matrix) +% dxpdk: Derivative of xp with respect to k ((2N)x4 matrix) +% +%Definitions: +%Let P be a point in 3D of coordinates X in the world reference frame (stored in the matrix X) +%The coordinate vector of P in the camera reference frame is: Xc = R*X + T +%where R is the rotation matrix corresponding to the rotation vector om: R = rodrigues(om); +%call x, y and z the 3 coordinates of Xc: x = Xc(1); y = Xc(2); z = Xc(3); +%The pinehole projection coordinates of P is [a;b] where a=x/z and b=y/z. +%call r^2 = a^2 + b^2. +%The distorted point coordinates are: xd = [xx;yy] where: +% +%xx = a * (1 + kc(1)*r^2 + kc(2)*r^4) + 2*kc(3)*a*b + kc(4)*(r^2 + 2*a^2); +%yy = b * (1 + kc(1)*r^2 + kc(2)*r^4) + kc(3)*(r^2 + 2*b^2) + 2*kc(4)*a*b; +% +%The left terms correspond to radial distortion, the right terms correspond to tangential distortion +% +%Fianlly, convertion into pixel coordinates: The final pixel coordinates vector xp=[xxp;yyp] where: +% +%xxp = f(1)*xx + c(1) +%yyp = f(2)*yy + c(2) +% +% +%NOTE: About 90 percent of the code takes care fo computing the Jacobian matrices +% +% +%Important function called within that program: +% +%rodrigues.m: Computes the rotation matrix corresponding to a rotation vector +% +%rigid_motion.m: Computes the rigid motion transformation of a given structure + + + +if nargin < 6, + k = zeros(4,1); + if nargin < 5, + c = zeros(2,1); + if nargin < 4, + f = ones(2,1); + if nargin < 3, + T = zeros(3,1); + if nargin < 2, + om = zeros(3,1); + if nargin < 1, + error('Need at least a 3D structure to project (in project_points.m)'); + return; + end; + end; + end; + end; + end; +end; + + +[m,n] = size(X); + +[Y,dYdom,dYdT] = rigid_motion(X,om,T); + + +inv_Z = 1./Y(3,:); + +x = (Y(1:2,:) .* (ones(2,1) * inv_Z)) ; + + +bb = (-x(1,:) .* inv_Z)'*ones(1,3); +cc = (-x(2,:) .* inv_Z)'*ones(1,3); + + +dxdom = zeros(2*n,3); +dxdom(1:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdom(1:3:end,:) + bb .* dYdom(3:3:end,:); +dxdom(2:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdom(2:3:end,:) + cc .* dYdom(3:3:end,:); + +dxdT = zeros(2*n,3); +dxdT(1:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdT(1:3:end,:) + bb .* dYdT(3:3:end,:); +dxdT(2:2:end,:) = ((inv_Z')*ones(1,3)) .* dYdT(2:3:end,:) + cc .* dYdT(3:3:end,:); + + +% Add distortion: + +r2 = x(1,:).^2 + x(2,:).^2; + + + +dr2dom = 2*((x(1,:)')*ones(1,3)) .* dxdom(1:2:end,:) + 2*((x(2,:)')*ones(1,3)) .* dxdom(2:2:end,:); +dr2dT = 2*((x(1,:)')*ones(1,3)) .* dxdT(1:2:end,:) + 2*((x(2,:)')*ones(1,3)) .* dxdT(2:2:end,:); + + +r4 = r2.^2; + +dr4dom = 2*((r2')*ones(1,3)) .* dr2dom; +dr4dT = 2*((r2')*ones(1,3)) .* dr2dT; + + +% Radial distortion: + +cdist = 1 + k(1) * r2 + k(2) * r4; + +dcdistdom = k(1) * dr2dom + k(2) * dr4dom; +dcdistdT = k(1) * dr2dT+ k(2) * dr4dT; +dcdistdk = [ r2' r4' zeros(n,2)]; + + +xd1 = x .* (ones(2,1)*cdist); + +dxd1dom = zeros(2*n,3); +dxd1dom(1:2:end,:) = (x(1,:)'*ones(1,3)) .* dcdistdom; +dxd1dom(2:2:end,:) = (x(2,:)'*ones(1,3)) .* dcdistdom; +coeff = (reshape([cdist;cdist],2*n,1)*ones(1,3)); +dxd1dom = dxd1dom + coeff.* dxdom; + +dxd1dT = zeros(2*n,3); +dxd1dT(1:2:end,:) = (x(1,:)'*ones(1,3)) .* dcdistdT; +dxd1dT(2:2:end,:) = (x(2,:)'*ones(1,3)) .* dcdistdT; +dxd1dT = dxd1dT + coeff.* dxdT; + +dxd1dk = zeros(2*n,4); +dxd1dk(1:2:end,:) = (x(1,:)'*ones(1,4)) .* dcdistdk; +dxd1dk(2:2:end,:) = (x(2,:)'*ones(1,4)) .* dcdistdk; + + + +% tangential distortion: + +a1 = 2.*x(1,:).*x(2,:); +a2 = r2 + 2*x(1,:).^2; +a3 = r2 + 2*x(2,:).^2; + +delta_x = [k(3)*a1 + k(4)*a2 ; + k(3) * a3 + k(4)*a1]; + + +ddelta_xdx = zeros(2*n,2*n); +aa = (2*k(3)*x(2,:)+6*k(4)*x(1,:))'*ones(1,3); +bb = (2*k(3)*x(1,:)+2*k(4)*x(2,:))'*ones(1,3); +cc = (6*k(3)*x(2,:)+2*k(4)*x(1,:))'*ones(1,3); + +ddelta_xdom = zeros(2*n,3); +ddelta_xdom(1:2:end,:) = aa .* dxdom(1:2:end,:) + bb .* dxdom(2:2:end,:); +ddelta_xdom(2:2:end,:) = bb .* dxdom(1:2:end,:) + cc .* dxdom(2:2:end,:); + +ddelta_xdT = zeros(2*n,3); +ddelta_xdT(1:2:end,:) = aa .* dxdT(1:2:end,:) + bb .* dxdT(2:2:end,:); +ddelta_xdT(2:2:end,:) = bb .* dxdT(1:2:end,:) + cc .* dxdT(2:2:end,:); + +ddelta_xdk = zeros(2*n,4); +ddelta_xdk(1:2:end,3) = a1'; +ddelta_xdk(1:2:end,4) = a2'; +ddelta_xdk(2:2:end,3) = a3'; +ddelta_xdk(2:2:end,4) = a1'; + + + +xd2 = xd1 + delta_x; + +dxd2dom = dxd1dom + ddelta_xdom ; +dxd2dT = dxd1dT + ddelta_xdT; +dxd2dk = dxd1dk + ddelta_xdk ; + + +% Pixel coordinates: + +xp = xd2 .* (f * ones(1,n)) + c*ones(1,n); + +coeff = reshape(f*ones(1,n),2*n,1); + +dxpdom = (coeff*ones(1,3)) .* dxd2dom; +dxpdT = (coeff*ones(1,3)) .* dxd2dT; +dxpdk = (coeff*ones(1,4)) .* dxd2dk; + +dxpdf = zeros(2*n,2); +dxpdf(1:2:end,1) = xd2(1,:)'; +dxpdf(2:2:end,2) = xd2(2,:)'; + +dxpdc = zeros(2*n,2); +dxpdc(1:2:end,1) = ones(n,1); +dxpdc(2:2:end,2) = ones(n,1); + + +return; + +% Test of the Jacobians: + +n = 10; + +X = 10*randn(3,n); +om = randn(3,1); +T = [10*randn(2,1);40]; +f = 1000*rand(2,1); +c = 1000*randn(2,1); +k = 0.5*randn(4,1); + + +[x,dxdom,dxdT,dxdf,dxdc,dxdk] = project_points(X,om,T,f,c,k); + + +% Test on om: NOT OK + +dom = 0.000000001 * norm(om)*randn(3,1); +om2 = om + dom; + +[x2] = project_points(X,om2,T,f,c,k); + +x_pred = x + reshape(dxdom * dom,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on T: OK!! + +dT = 0.0001 * norm(T)*randn(3,1); +T2 = T + dT; + +[x2] = project_points(X,om,T2,f,c,k); + +x_pred = x + reshape(dxdT * dT,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + + +% Test on f: OK!! + +df = 0.001 * norm(f)*randn(2,1); +f2 = f + df; + +[x2] = project_points(X,om,T,f2,c,k); + +x_pred = x + reshape(dxdf * df,2,n); + + +norm(x2-x)/norm(x2 - x_pred) + + +% Test on c: OK!! + +dc = 0.01 * norm(c)*randn(2,1); +c2 = c + dc; + +[x2] = project_points(X,om,T,f,c2,k); + +x_pred = x + reshape(dxdc * dc,2,n); + +norm(x2-x)/norm(x2 - x_pred) + +% Test on k: OK!! + +dk = 0.001 * norm(4)*randn(4,1); +k2 = k + dk; + +[x2] = project_points(X,om,T,f,c,k2); + +x_pred = x + reshape(dxdk * dk,2,n); + +norm(x2-x)/norm(x2 - x_pred) diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/projectedGrid.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/projectedGrid.m new file mode 100755 index 0000000..561a7d0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/projectedGrid.m @@ -0,0 +1,24 @@ +function [XX,H] = projectedGrid ( P1, P2, P3, P4 , nx, ny); + +% new formalism using homographies + +a00 = [P1;1]; +a10 = [P2;1]; +a11 = [P3;1]; +a01 = [P4;1]; + +% Compute the planart collineation: + +[H] = compute_collineation (a00, a10, a11, a01); + + +% Build the grid using the planar collineation: + +x_l = ((0:(nx-1))'*ones(1,ny))/(nx-1); +y_l = (ones(nx,1)*(0:(ny-1)))/(ny-1); + +pts = [x_l(:) y_l(:) ones(nx*ny,1)]'; + +XX = H*pts; + +XX = XX(1:2,:) ./ (ones(2,1)*XX(3,:)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/readras.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/readras.m new file mode 100755 index 0000000..fc1820b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/readras.m @@ -0,0 +1,87 @@ +function [X, map] = readras(filename, ys, ye, xs, xe); +%READRAS Read an image file in sun raster format. +% READRAS('imagefile.ras') reads a "sun.raster" image file. +% [X, map] = READRAS('imagefile.ras') returns both the image and a +% color map, so that +% [X, map] = readras('imagefile.ras'); +% image(X) +% colormap(map) +% axis('equal') +% will display the result with the proper colors. +% NOTE: readras cannot deal with complicated color maps. +% In fact, Matlab doesn't quite allow to work with colormaps +% with more than 64 entries. +% + +%% +%% (C) Thomas K. Leung 3/30/93. +%% California Institute of Technology. +%% Modified by Andrea Mennucci to deal with color images +%% + +% PC and UNIX version of readras - Jean-Yves Bouguet - Dec. 1998 + +dot = max(find(filename == '.')); +suffix = filename(dot+1:dot+3); + +if(strcmp(suffix, 'ras')) % raster file format % + fp = fopen(filename, 'rb'); + if(fp<0) error(['Cannot open ' filename '.']), end + + %Read and crack the 32-byte header + fseek(fp, 4, -1); + + width = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + height = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + depth = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + length = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + type = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + maptype = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + maplen = 2^24 * fread(fp, 1, 'uchar') + 2^16 * fread(fp, 1, 'uchar') + 2^8 * fread(fp, 1, 'uchar') + fread(fp, 1, 'uchar'); + + maplen = maplen / 3; + + if maptype == 2 % RMT_RAW + map = fread(fp, [maplen, 3], 'uchar')/255; +% if maplen<64, map=[map',zeros(3,64-maplen)]';maplen=64; end; + elseif maptype == 1 % RMT_EQUAL_RGB + map(:,1) = fread(fp, [maplen], 'uchar'); + map(:,2) = fread(fp, [maplen], 'uchar'); + map(:,3) = fread(fp, [maplen], 'uchar'); + %maxmap = max(max(map)); + map = map/255; + if maplen<64, map=[map',zeros(3,64-maplen)]'; maplen=64; end; + else % RMT_NONE + map = []; + end +% if maplen>64, +% map=[map',zeros(3,256-maplen)]'; +% end; + + % Read the image + + if rem(width,2) == 1 + Xt = fread(fp, [width+1, height], 'uchar'); + X = Xt(1:width, :)'; + else + Xt = fread(fp, [width, height], 'uchar'); + X = Xt'; + end + X = X + 1; + fclose(fp); +else + error('Image file name must end in either ''ras'' or ''rast''.'); +end + + +if nargin == 5 + + X = X(ys:ye, xs:xe); + +end \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/recomp_corner_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/recomp_corner_calib.m new file mode 100755 index 0000000..e0af501 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/recomp_corner_calib.m @@ -0,0 +1,96 @@ +% Re-select te corners after calibration + +check_active_images; + +if ~exist(['y_' num2str(ind_active(1))]), + fprintf(1,'Need to calibrate once before before recomputing image corners. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + +if ~exist(['I_' num2str(ind_active(1))]), + ima_read_calib; + if no_image_file, + disp('Cannot extract corners without images'); + return; + end; +end; + +fprintf(1,'\nRe-extraction of the grid corners on the images (after first calibration)\n'); + +disp('Window size for corner finder (wintx and winty):'); +wintx = input('wintx ([] = 5) = '); +if isempty(wintx), wintx = 5; end; +wintx = round(wintx); +winty = input('winty ([] = 5) = '); +if isempty(winty), winty = 5; end; +winty = round(winty); + +fprintf(1,'Window size = %dx%d\n',2*wintx+1,2*winty+1); + +ima_numbers = input('Number(s) of image(s) to process ([] = all images) = '); + +if isempty(ima_numbers), + ima_proc = 1:n_ima; +else + ima_proc = ima_numbers; +end; + +q_auto = input('Use the projection of 3D grid or manual click ([]=auto, other=manual): '); + +fprintf(1,'Processing image '); + +for kk = ima_proc; + + if active_images(kk), + + fprintf(1,'%d...',kk); + + if isempty(q_auto), + + eval(['I = I_' num2str(kk) ';']); + + eval(['y = y_' num2str(kk) ';']); + + xc = cornerfinder(y+1,I,winty,wintx); % the four corners + + eval(['wintx_' num2str(kk) ' = wintx;']); + eval(['winty_' num2str(kk) ' = winty;']); + + eval(['x_' num2str(kk) '= xc - 1;']); + + else + + fprintf(1,'\n'); + + click_ima_calib; + + end; + + else + + if ~exist(['omc_' num2str(kk)]), + + eval(['dX_' num2str(kk) ' = NaN;']); + eval(['dY_' num2str(kk) ' = NaN;']); + + eval(['wintx_' num2str(kk) ' = NaN;']); + eval(['winty_' num2str(kk) ' = NaN;']); + + eval(['x_' num2str(kk) ' = NaN*ones(2,1);']); + eval(['X_' num2str(kk) ' = NaN*ones(3,1);']); + + eval(['n_sq_x_' num2str(kk) ' = NaN;']); + eval(['n_sq_y_' num2str(kk) ' = NaN;']); + + end; + + end; + + +end; + +% Recompute the error: + +comp_error_calib; + +fprintf(1,'\ndone\n'); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rect.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rect.m new file mode 100755 index 0000000..d8b6366 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rect.m @@ -0,0 +1,93 @@ +function [Irec] = rect(I,R,f,c,k,KK_new); + + +% Note: R is the motion of the points in space +% So: X2 = R*X where X: coord in the old reference frame, X2: coord in the new ref frame. + +[nr,nc] = size(I); + +Irec = 255*ones(nr,nc); + +[mx,my] = meshgrid(1:nc, 1:nr); +px = reshape(mx',nc*nr,1); +py = reshape(my',nc*nr,1); + +rays = inv(KK_new)*[(px - 1)';(py - 1)';ones(1,length(px))]; + + +% Rotation: (or affine transformation): + +rays2 = R'*rays; + + +x = [rays2(1,:)./rays2(3,:);rays2(2,:)./rays2(3,:)]; + +% Add distortion: + +k1 = k(1); +k2 = k(2); + +p1 = k(3); +p2 = k(4); + +r_2 = sum(x.^2); + +delta_x = [2*p1*x(1,:).*x(2,:) + p2*(r_2 + 2*x(1,:).^2) ; + p1 * (r_2 + 2*x(2,:).^2)+2*p2*x(1,:).*x(2,:)]; + +xd = (ones(2,1)*( 1 + k1 * r_2 + k2 * r_2.^2)) .* x + delta_x; + + +% Reconvert in pixels: + +px2 = f(1)*xd(1,:)+c(1); +py2 = f(2)*xd(2,:)+c(2); + + +% Interpolate between the closest pixels: + + +px_0 = floor(px2); +px_1 = px_0 + 1; +alpha_x = px2 - px_0; + +py_0 = floor(py2); +py_1 = py_0 + 1; +alpha_y = py2 - py_0; + +good_points = find((px_0 >= 0) & (px_1 <= (nc-1)) & (py_0 >= 0) & (py_1 <= (nr-1))); + +I_lu = I(px_0(good_points) * nr + py_0(good_points) + 1); +I_ru = I(px_1(good_points) * nr + py_0(good_points) + 1); +I_ld = I(px_0(good_points) * nr + py_1(good_points) + 1); +I_rd = I(px_1(good_points) * nr + py_1(good_points) + 1); + + +I_interp = (1 - alpha_y(good_points)).*((1 - alpha_x(good_points)).* I_lu + alpha_x(good_points) .* I_ru) + alpha_y(good_points) .* ((1 - alpha_x(good_points)).* I_ld + alpha_x(good_points) .* I_rd); + + +Irec((px(good_points)-1)*nr + py(good_points)) = I_interp; + + + +return; + + +% Convert in indices: + +fact = 3; + +[XX,YY]= meshgrid(1:nc,1:nr); +[XXi,YYi]= meshgrid(1:1/fact:nc,1:1/fact:nr); + +%tic; +Iinterp = interp2(XX,YY,I,XXi,YYi); +%toc + +[nri,nci] = size(Iinterp); + + +ind_col = round(fact*(f(1)*xd(1,:)+c(1)))+1; +ind_row = round(fact*(f(2)*xd(2,:)+c(2)))+1; + +good_points = find((ind_col >=1)&(ind_col<=nci)&(ind_row >=1)& (ind_row <=nri)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/reproject_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/reproject_calib.m new file mode 100755 index 0000000..86b13f5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/reproject_calib.m @@ -0,0 +1,92 @@ +%%%%%%%%%%%%%%%%%%%% REPROJECT ON THE IMAGES %%%%%%%%%%%%%%%%%%%%%%%% + +if ~exist('no_image'), + no_image = 0; +end; + +check_active_images; + + +% Color code for each image: + +colors = 'brgkcm'; + +% Reproject the patterns on the images, and compute the pixel errors: + +% Reload the images if necessary + +if ~exist(['omc_' num2str(ind_active(1)) ]), + fprintf(1,'Need to calibrate before showing image reprojection. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + +if ~no_image, + if ~exist(['I_' num2str(ind_active(1)) ]'), + ima_read_calib; + if no_image_file, + fprintf(1,'WARNING: Do not show the original images\n'); %return; + end; + end; +else + no_image_file = 1; +end; + + + +ima_numbers = input('Number(s) of image(s) to show ([] = all images) = '); + +if isempty(ima_numbers), + ima_proc = 1:n_ima; +else + ima_proc = ima_numbers; +end; + + +figure(5); +for kk = ima_proc, %1:n_ima, + if active_images(kk) & eval(['~isnan(y_' num2str(kk) '(1,1))']), + eval(['plot(ex_' num2str(kk) '(1,:),ex_' num2str(kk) '(2,:),''' colors(rem(kk-1,6)+1) '+'');']); + hold on; + end; +end; +hold off; +axis('equal'); +title('Reprojection error (in pixel)'); +xlabel('x'); +ylabel('y'); +drawnow; + +set(5,'Name','error','NumberTitle','off'); + + + +for kk = ima_proc, + + if active_images(kk) & eval(['~isnan(y_' num2str(kk) '(1,1))']), + + if exist(['I_' num2str(kk)]), + eval(['I = I_' num2str(kk) ';']); + else + I = 255*ones(ny,nx); + end; + + figure(5+kk); + image(I); hold on; + colormap(gray(256)); + title(['Image ' num2str(kk) ' - Image points (+) and reprojected grid points (o)']); + eval(['plot(x_' num2str(kk) '(1,:)+1,x_' num2str(kk) '(2,:)+1,''r+'');']); + eval(['plot(y_' num2str(kk) '(1,:)+1,y_' num2str(kk) '(2,:)+1,''' colors(rem(kk-1,6)+1) 'o'');']); + zoom on; + hold off; + drawnow; + + set(5+kk,'Name',num2str(kk),'NumberTitle','off'); + + end; + +end; + + +err_std = std(ex')'; + +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rigid_motion.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rigid_motion.m new file mode 100755 index 0000000..473405c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rigid_motion.m @@ -0,0 +1,66 @@ +function [Y,dYdom,dYdT] = rigid_motion(X,om,T); + +%rigid_motion.m +% +%[Y,dYdom,dYdT] = rigid_motion(X,om,T) +% +%Computes the rigid motion transformation Y = R*X+T, where R = rodrigues(om). +% +%INPUT: X: 3D structure in the world coordinate frame (3xN matrix for N points) +% (om,T): Rigid motion parameters between world coordinate frame and camera reference frame +% om: rotation vector (3x1 vector); T: translation vector (3x1 vector) +% +%OUTPUT: Y: 3D coordinates of the structure points in the camera reference frame (3xN matrix for N points) +% dYdom: Derivative of Y with respect to om ((3N)x3 matrix) +% dYdT: Derivative of Y with respect to T ((3N)x3 matrix) +% +%Definitions: +%Let P be a point in 3D of coordinates X in the world reference frame (stored in the matrix X) +%The coordinate vector of P in the camera reference frame is: Y = R*X + T +%where R is the rotation matrix corresponding to the rotation vector om: R = rodrigues(om); +% +%Important function called within that program: +% +%rodrigues.m: Computes the rotation matrix corresponding to a rotation vector + + + +if nargin < 3, + T = zeros(3,1); + if nargin < 2, + om = zeros(3,1); + if nargin < 1, + error('Need at least a 3D structure as input (in rigid_motion.m)'); + return; + end; + end; +end; + + +[R,dRdom] = rodrigues(om); + +[m,n] = size(X); + +Y = R*X + T*ones(1,n); + +if nargout > 1, + + +dYdR = zeros(3*n,9); +dYdT = zeros(3*n,3); + +dYdR(1:3:end,1:3:end) = X'; +dYdR(2:3:end,2:3:end) = X'; +dYdR(3:3:end,3:3:end) = X'; + +dYdT(1:3:end,1) = ones(n,1); +dYdT(2:3:end,2) = ones(n,1); +dYdT(3:3:end,3) = ones(n,1); + +dYdom = dYdR * dRdom; + +end; + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rodrigues.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rodrigues.m new file mode 100755 index 0000000..9d55337 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rodrigues.m @@ -0,0 +1,217 @@ +function [out,dout]=rodrigues(in) + +% RODRIGUES Transform rotation matrix into rotation vector and viceversa. +% +% Sintax: [OUT]=RODRIGUES(IN) +% If IN is a 3x3 rotation matrix then OUT is the +% corresponding 3x1 rotation vector +% if IN is a rotation 3-vector then OUT is the +% corresponding 3x3 rotation matrix +% + +%% +%% Copyright (c) March 1993 -- Pietro Perona +%% California Institute of Technology +%% + +%% ALL CHECKED BY JEAN-YVES BOUGUET, October 1995. +%% FOR ALL JACOBIAN MATRICES !!! LOOK AT THE TEST AT THE END !! + +%% BUG when norm(om)=pi fixed -- April 6th, 1997; +%% Jean-Yves Bouguet + + +[m,n] = size(in); +%bigeps = 10e+4*eps; +bigeps = 10e+20*eps; + +if ((m==1) & (n==3)) | ((m==3) & (n==1)) %% it is a rotation vector + theta = norm(in); + if theta < eps + R = eye(3); + + %if nargout > 1, + + dRdin = [0 0 0; + 0 0 1; + 0 -1 0; + 0 0 -1; + 0 0 0; + 1 0 0; + 0 1 0; + -1 0 0; + 0 0 0]; + + %end; + + else + if n==length(in) in=in'; end; %% make it a column vec. if necess. + + %m3 = [in,theta] + + dm3din = [eye(3);in'/theta]; + + omega = in/theta; + + %m2 = [omega;theta] + + dm2dm3 = [eye(3)/theta -in/theta^2; zeros(1,3) 1]; + + alpha = cos(theta); + beta = sin(theta); + gamma = 1-cos(theta); + omegav=[[0 -omega(3) omega(2)];[omega(3) 0 -omega(1)];[-omega(2) omega(1) 0 ]]; + A = omega*omega'; + + %m1 = [alpha;beta;gamma;omegav;A]; + + dm1dm2 = zeros(21,4); + dm1dm2(1,4) = -sin(theta); + dm1dm2(2,4) = cos(theta); + dm1dm2(3,4) = sin(theta); + dm1dm2(4:12,1:3) = [0 0 0 0 0 1 0 -1 0; + 0 0 -1 0 0 0 1 0 0; + 0 1 0 -1 0 0 0 0 0]'; + + w1 = omega(1); + w2 = omega(2); + w3 = omega(3); + + dm1dm2(13:21,1) = [2*w1;w2;w3;w2;0;0;w3;0;0]; + dm1dm2(13: 21,2) = [0;w1;0;w1;2*w2;w3;0;w3;0]; + dm1dm2(13:21,3) = [0;0;w1;0;0;w2;w1;w2;2*w3]; + + R = eye(3)*alpha + omegav*beta + A*gamma; + + dRdm1 = zeros(9,21); + + dRdm1([1 5 9],1) = ones(3,1); + dRdm1(:,2) = omegav(:); + dRdm1(:,4:12) = beta*eye(9); + dRdm1(:,3) = A(:); + dRdm1(:,13:21) = gamma*eye(9); + + dRdin = dRdm1 * dm1dm2 * dm2dm3 * dm3din; + + + end; + out = R; + dout = dRdin; + + %% it is prob. a rot matr. + elseif ((m==n) & (m==3) & (norm(in' * in - eye(3)) < bigeps)... + & (abs(det(in)-1) < bigeps)) + R = in; + + + + tr = (trace(R)-1)/2; + dtrdR = [1 0 0 0 1 0 0 0 1]/2; + theta = real(acos(tr)); + + + if sin(theta) >= 1e-5, + + dthetadtr = -1/sqrt(1-tr^2); + + dthetadR = dthetadtr * dtrdR; + % var1 = [vth;theta]; + vth = 1/(2*sin(theta)); + dvthdtheta = -vth*cos(theta)/sin(theta); + dvar1dtheta = [dvthdtheta;1]; + + dvar1dR = dvar1dtheta * dthetadR; + + + om1 = [R(3,2)-R(2,3), R(1,3)-R(3,1), R(2,1)-R(1,2)]'; + + dom1dR = [0 0 0 0 0 1 0 -1 0; + 0 0 -1 0 0 0 1 0 0; + 0 1 0 -1 0 0 0 0 0]; + + % var = [om1;vth;theta]; + dvardR = [dom1dR;dvar1dR]; + + % var2 = [om;theta]; + om = vth*om1; + domdvar = [vth*eye(3) om1 zeros(3,1)]; + dthetadvar = [0 0 0 0 1]; + dvar2dvar = [domdvar;dthetadvar]; + + + out = om*theta; + domegadvar2 = [theta*eye(3) om]; + + dout = domegadvar2 * dvar2dvar * dvardR; + + + else + if tr > 0; % case norm(om)=0; + + out = [0 0 0]'; + + dout = [0 0 0 0 0 1/2 0 -1/2 0; + 0 0 -1/2 0 0 0 1/2 0 0; + 0 1/2 0 -1/2 0 0 0 0 0]; + else % case norm(om)=pi; %% fixed April 6th + + + out = theta * (sqrt((diag(R)+1)/2).*[1;2*(R(1,2:3)>=0)'-1]); + %keyboard; + + if nargout > 1, + fprintf(1,'WARNING!!!! Jacobian domdR undefined!!!\n'); + dout = NaN*ones(3,9); + end; + end; + end; + + else + error('Neither a rotation matrix nor a rotation vector were provided'); + end; + +return; + +%% test of the Jacobians: + +%%%% TEST OF dRdom: +om = randn(3,1); +dom = randn(3,1)/1000000; + +[R1,dR1] = rodrigues(om); +R2 = rodrigues(om+dom); + +R2a = R1 + reshape(dR1 * dom,3,3); + +gain = norm(R2 - R1)/norm(R2 - R2a) + +%%% TEST OF dOmdR: +om = randn(3,1); +R = rodrigues(om); +dom = randn(3,1)/10000; +dR = rodrigues(om+dom) - R; + +[omc,domdR] = rodrigues(R); +[om2] = rodrigues(R+dR); + +om_app = omc + domdR*dR(:); + +gain = norm(om2 - omc)/norm(om2 - om_app) + + +%%% OTHER BUG: (FIXED NOW!!!) + +omu = randn(3,1); +omu = omu/norm(omu) +om = pi*omu; +[R,dR]= rodrigues(om); +[om2] = rodrigues(R); +[om om2] + +%%% NORMAL OPERATION + +om = randn(3,1); +[R,dR]= rodrigues(om); +[om2] = rodrigues(R); +[om om2] + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rotation.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rotation.m new file mode 100755 index 0000000..87ee2fe --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/rotation.m @@ -0,0 +1,23 @@ +function [] = rotation(st); + +if nargin < 1, + st= 1; +end; + + +fig = gcf; + +ax = gca; + +vv = get(ax,'view'); + +az = vv(1); +el = vv(2); + +for azi = az:-abs(st):az-360, + + set(ax,'view',[azi el]); + figure(fig); + drawnow; + +end; diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/run_error_analysis.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/run_error_analysis.m new file mode 100755 index 0000000..095e17e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/run_error_analysis.m @@ -0,0 +1,65 @@ +%%% Program that launchs the complete + +for N_ima_active = 1:30, + + error_analysis; + +end; + + + +return; + + +f = []; +f_std = []; + +c = []; +c_std = []; + +k = []; +k_std = []; + +NN = 30; + +for rr = 1:NN, + + load(['Calib_Accuracies_' num2str(rr)]); + + [m1,s1] = mean_std_robust(fc_list(1,:)); + [m2,s2] = mean_std_robust(fc_list(2,:)); + + f = [f [m1;m2]]; + f_std = [f_std [s1;s2]]; + + [m1,s1] = mean_std_robust(cc_list(1,:)); + [m2,s2] = mean_std_robust(cc_list(2,:)); + + c = [c [m1;m2]]; + c_std = [c_std [s1;s2]]; + + [m1,s1] = mean_std_robust(kc_list(1,:)); + [m2,s2] = mean_std_robust(kc_list(2,:)); + [m3,s3] = mean_std_robust(kc_list(3,:)); + [m4,s4] = mean_std_robust(kc_list(4,:)); + + k = [k [m1;m2;m3;m4]]; + k_std = [k_std [s1;s2;s3;s4]]; + +end; + +figure(1); +errorbar([1:NN;1:NN]',f',f_std'); +figure(2); +errorbar([1:NN;1:NN]',c',c_std'); +figure(3); +errorbar([1:NN;1:NN;1:NN;1:NN]',k',k_std'); + +figure(4); +semilogy(f_std'); + +figure(5); +semilogy(c_std'); + +figure(6); +semilogy(k_std'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveinr.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveinr.m new file mode 100755 index 0000000..a176e39 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveinr.m @@ -0,0 +1,46 @@ +%SAVEINR Write an INRIMAGE format file +% +% SAVEINR(filename, im) +% +% Saves the specified image array in a INRIA image format file. +% +% SEE ALSO: loadinr +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + +% Peter Corke 1996 + +function saveinr(fname, im) + + fid = fopen(fname, 'w'); + [r,c] = size(im'); + + % build the header + hdr = []; + s = sprintf('#INRIMAGE-4#{\n'); + hdr = [hdr s]; + s = sprintf('XDIM=%d\n',c); + hdr = [hdr s]; + s = sprintf('YDIM=%d\n',r); + hdr = [hdr s]; + s = sprintf('ZDIM=1\n'); + hdr = [hdr s]; + s = sprintf('VDIM=1\n'); + hdr = [hdr s]; + s = sprintf('TYPE=float\n'); + hdr = [hdr s]; + s = sprintf('PIXSIZE=32\n'); + hdr = [hdr s]; + s = sprintf('SCALE=2**0\n'); + hdr = [hdr s]; + s = sprintf('CPU=sun\n#'); + hdr = [hdr s]; + + % make it 256 bytes long and write it + hdr256 = zeros(1,256); + hdr256(1:length(hdr)) = hdr; + fwrite(fid, hdr256, 'uchar'); + + % now the binary data + fwrite(fid, im', 'float32'); + fclose(fid) diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/savepgm.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/savepgm.m new file mode 100755 index 0000000..397f028 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/savepgm.m @@ -0,0 +1,22 @@ +%SAVEPGM Write a PGM format file +% +% SAVEPGM(filename, im) +% +% Saves the specified image array in a binary (P5) format PGM image file. +% +% SEE ALSO: loadpgm +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function savepgm(fname, im) + + fid = fopen(fname, 'w'); + [r,c] = size(im'); + fprintf(fid, 'P5\n'); + fprintf(fid, '%d %d\n', r, c); + fprintf(fid, '255\n'); + fwrite(fid, im', 'uchar'); + fclose(fid) diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveppm.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveppm.m new file mode 100755 index 0000000..0062ee0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saveppm.m @@ -0,0 +1,25 @@ +%SAVEPPM Write a PPM format file +% +% SAVEPPM(filename, r, g, b) +% +% Saves the specified red, green and blue planes in a binary (P6) +% format PPM image file. +% +% SEE ALSO: loadppm +% +% Copyright (c) Peter Corke, 1999 Machine Vision Toolbox for Matlab + + +% Peter Corke 1994 + +function saveppm(fname, R, G, B) + + fid = fopen(fname, 'w'); + [r,c] = size(R'); + fprintf(fid, 'P6\n'); + fprintf(fid, '%d %d\n', r, c); + fprintf(fid, '255\n'); + im = [R(:) G(:) B(:)]; + im = reshape(c,r); + fwrite(fid, im, 'uchar'); + fclose(fid) diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saving_calib.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saving_calib.m new file mode 100755 index 0000000..e0575e0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/saving_calib.m @@ -0,0 +1,27 @@ +fprintf(1,'\nSaving calibration results under Calib_Results.mat\n'); + +check_active_images; + +if ~exist('solution_init'), solution_init = []; end; + +for kk = 1:n_ima, + if ~exist(['dX_' num2str(kk)]), eval(['dX_' num2str(kk) '= dX;']); end; + if ~exist(['dY_' num2str(kk)]), eval(['dY_' num2str(kk) '= dY;']); end; +end; + +if ~exist('param_list'), + param_list = solution; +end; + + +string_save = 'save Calib_Results param_list active_images ind_active fc kc cc ex x y solution sol_with_center solution_init wintx winty n_ima type_numbering N_slots small_calib_image first_num image_numbers format_image calib_name Hcal Wcal nx ny map dX_default dY_default KK inv_KK dX dY'; + +for kk = 1:n_ima, + string_save = [string_save ' X_' num2str(kk) ' x_' num2str(kk) ' y_' num2str(kk) ' ex_' num2str(kk) ' omc_' num2str(kk) ' Rc_' num2str(kk) ' Tc_' num2str(kk) ' H_' num2str(kk) ' n_sq_x_' num2str(kk) ' n_sq_y_' num2str(kk) ' wintx_' num2str(kk) ' winty_' num2str(kk) ' dX_' num2str(kk) ' dY_' num2str(kk)]; +end; + +%fprintf(1,'To load later click on Load\n'); + +fprintf(1,'done\n'); + +eval(string_save); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/script_fit_distortion.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/script_fit_distortion.m new file mode 100755 index 0000000..c5e5430 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/script_fit_distortion.m @@ -0,0 +1,39 @@ + + satis_distort = 0; + + disp(['Estimated focal: ' num2str(f_g) ' pixels']); + + while ~satis_distort, + + k_g = input('Guess for distortion factor kc ([]=0): '); + + if isempty(k_g), k_g = 0; end; + + xy_corners_undist = comp_distortion2([x' - c_g(1);y'-c_g(2)]/f_g,k_g); + + xu = xy_corners_undist(1,:)'; + yu = xy_corners_undist(2,:)'; + + [XXu] = projectedGrid ( [xu(1);yu(1)], [xu(2);yu(2)],[xu(3);yu(3)], [xu(4);yu(4)],n_sq_x+1,n_sq_y+1); % The full grid + + XX = (ones(2,1)*(1 + k_g * sum(XXu.^2))) .* XXu; + XX(1,:) = f_g*XX(1,:)+c_g(1); + XX(2,:) = f_g*XX(2,:)+c_g(2); + + figure(2); + image(I); + colormap(map); + zoom on; + hold on; + %plot(f_g*XXu(1,:)+c_g(1),f_g*XXu(2,:)+c_g(2),'ro'); + plot(XX(1,:),XX(2,:),'r+'); + title('The red crosses should be on the grid corners...'); + hold off; + + satis_distort = input('Satisfied with distortion? ([]=no, other=yes) '); + + satis_distort = ~isempty(satis_distort); + + + end; + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center.m new file mode 100755 index 0000000..15508e5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center.m @@ -0,0 +1,19 @@ +%%% Selection of the calibration solution with center estimation + +if ~exist('sol_no_center'), + fprintf(1,'Need to calibrate before selecting solution without center. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + +solution = sol_no_center; + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters; +comp_error_calib; + +fprintf(1,'\n\nCalibration results without principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center3D.m new file mode 100755 index 0000000..070d81c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_no_center3D.m @@ -0,0 +1,20 @@ +%%% Selection of the calibration solution with center estimation + +solution = sol_no_center; + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters3D; + + +fprintf(1,'\n\nCalibration results without principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +graphout_calib3D; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center.m new file mode 100755 index 0000000..2df9ba8 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center.m @@ -0,0 +1,19 @@ +%%% Selection of the calibration solution with center estimation + +if ~exist('sol_with_center'), + fprintf(1,'Need to calibrate before selecting solution with center. Maybe need to load Calib_Results.mat file.\n'); + return; +end; + +solution = sol_with_center; + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters; +comp_error_calib; + +fprintf(1,'\n\nCalibration results with principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); +fprintf(1,'Pixel error: err = [ %3.5f %3.5f]\n\n',err_std); diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center3D.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center3D.m new file mode 100755 index 0000000..eb6f4bf --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/select_sol_with_center3D.m @@ -0,0 +1,20 @@ +%%% Selection of the calibration solution with center estimation + +solution = sol_with_center; + +%%% Extraction of the final intrinsic and extrinsic paramaters: + +extract_parameters3D; + + +fprintf(1,'\n\nCalibration results with principal point estimation:\n\n'); +fprintf(1,'Focal Length: fc = [ %3.5f %3.5f]\n',fc); +fprintf(1,'Principal point: cc = [ %3.5f %3.5f]\n',cc); +fprintf(1,'Distortion: kc = [ %3.5f %3.5f %3.5f %3.5f]\n',kc); + + +%%%%%%%%%%%%%%%%%%%% GRAPHICAL OUTPUT %%%%%%%%%%%%%%%%%%%%%%%% + +graphout_calib3D; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/startup.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/startup.m new file mode 100755 index 0000000..aad0fa4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/startup.m @@ -0,0 +1,9 @@ +% Main camera calibration toolbox: + +calib_gui; + +%calib_gui; + +path(pwd,path); + +format compact diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/test_3d.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/test_3d.m new file mode 100755 index 0000000..9f442f4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/test_3d.m @@ -0,0 +1,80 @@ +Rc_1 = rodrigues(omc_1); +Rc_2 = rodrigues(omc_2); +Rc_3 = rodrigues(omc_3); +Rc_4 = rodrigues(omc_4); +Rc_5 = rodrigues(omc_5); +Rc_6 = rodrigues(omc_6); +Rc_7 = rodrigues(omc_7); +Rc_8 = rodrigues(omc_8); +Rc_9 = rodrigues(omc_9); + +Rc_10 = rodrigues(omc_10); +Rc_11 = rodrigues(omc_11); +Rc_12 = rodrigues(omc_12); +Rc_13 = rodrigues(omc_13); +Rc_14 = rodrigues(omc_14); +Rc_15 = rodrigues(omc_15); +Rc_16 = rodrigues(omc_16); +Rc_17 = rodrigues(omc_17); +Rc_18 = rodrigues(omc_18); + + + +RR1 = Rc_1'*Rc_10; % should be rodrigues([0;pi/2;0]) +TT1 = Rc_1'*(Tc_10-Tc_1); % should be [dX*n_sq_x_1;0;0] + +Xr_1 = RR1 * X_10 + TT1*ones(1,length(X_10)); + +figure(1); +plot3(X_1(1,:),X_1(2,:),X_1(3,:),'r+'); hold on; +plot3(Xr_1(1,:),Xr_1(2,:),Xr_1(3,:),'g+'); +hold off; +axis('equal'); +rotate3d on; +view(0,0); +xlabel('x'); +ylabel('y'); +zlabel('z'); + +aaa = []; + +RR1 = Rc_1'*Rc_10; % should be rodrigues([0;pi/2;0]) +TT1 = Rc_1'*(Tc_10-Tc_1); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR1) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + +RR2 = Rc_2'*Rc_11; % should be rodrigues([0;pi/2;0]) +TT2 = Rc_2'*(Tc_11-Tc_2); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR2) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + +RR3 = Rc_3'*Rc_12; % should be rodrigues([0;pi/2;0]) +TT3 = Rc_3'*(Tc_12-Tc_3); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR3) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + +RR4 = Rc_4'*Rc_13; % should be rodrigues([0;pi/2;0]) +TT4 = Rc_4'*(Tc_13-Tc_4); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR4) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + +RR5 = Rc_5'*Rc_14; % should be rodrigues([0;pi/2;0]) +TT5 = Rc_5'*(Tc_14-Tc_5); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR5) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + +RR6 = Rc_6'*Rc_15; % should be rodrigues([0;pi/2;0]) +TT6 = Rc_6'*(Tc_15-Tc_6); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR6) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + +RR7 = Rc_7'*Rc_16; % should be rodrigues([0;pi/2;0]) +TT7 = Rc_7'*(Tc_16-Tc_7); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR7) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + +RR8 = Rc_8'*Rc_17; % should be rodrigues([0;pi/2;0]) +TT8 = Rc_8'*(Tc_17-Tc_8); % should be [dX*n_sq_x_1;0;0] +err = rodrigues(RR8) - [0;pi/2;0] +aaa = [aaa 2*sin(err(2)/2)*.33*1000]; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/undistort_image.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/undistort_image.m new file mode 100755 index 0000000..6393d78 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/undistort_image.m @@ -0,0 +1,88 @@ +%%% INPUT THE IMAGE FILE NAME: + +dir; + +fprintf(1,'\n'); +disp('Program that undistort an image'); +disp('The intrinsic camera parameters are assumed to be known (previously computed)'); + +fprintf(1,'\n'); +image_name = input('Image name (full name without extension): ','s'); + +format_image2 = '0'; + +while format_image2 == '0', + + format_image2 = input('Image format: ([]=''r''=''ras'', ''b''=''bmp'', ''t''=''tif'', ''p''=''pgm'', ''j''=''jpg'') ','s'); + + if isempty(format_image2), + format_image2 = 'ras'; + end; + + if lower(format_image2(1)) == 'b', + format_image2 = 'bmp'; + else + if lower(format_image2(1)) == 't', + format_image2 = 'tif'; + else + if lower(format_image2(1)) == 'p', + format_image2 = 'pgm'; + else + if lower(format_image2(1)) == 'j', + format_image2 = 'jpg'; + else + if lower(format_image2(1)) == 'r', + format_image2 = 'ras'; + else + disp('Invalid image format'); + format_image2 = '0'; % Ask for format once again + end; + end; + end; + end; + end; +end; + +ima_name = [image_name '.' format_image]; + + + +%%% READ IN IMAGE: + +if format_image(1) == 'p', + I = double(pgmread(ima_name)); +else + if format_image(1) == 'r', + I = readras(ima_name); + else + I = double(imread(ima_name)); + end; +end; + +if size(I,3)>1, + I = I(:,:,2); +end; + + +%% SHOW THE ORIGINAL IMAGE: + +figure(2); +image(I); +colormap(gray(256)); +title('Original image (with distortion) - Stored in array I'); +drawnow; + + +%% UNDISTORT THE IMAGE: + +fprintf(1,'Compututing the undistorted image...\n') + +[I2] = rect(I,eye(3),fc,cc,kc,KK); + + +figure(3); +image(I2); +colormap(gray(256)); +title('Undistorted image - Stored in array I2'); +drawnow; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/writeras.m b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/writeras.m new file mode 100755 index 0000000..c7eb7bc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj/writeras.m @@ -0,0 +1,105 @@ +function writeras(filename, image, map); +%WRITERAS Write an image file in sun raster format. +% WRITERAS('imagefile.ras', image_matrix, map) writes a +% "sun.raster" image file. + +% Written by Thomas K. Leung 3/30/93. +% @ California Institute of Technology. + + +% PC and UNIX version of writeras - Jean-Yves Bouguet - Dec. 1998 + +dot = max(find(filename == '.')); +suffix = filename(dot+1:dot+3); + +if nargin < 3, + map = []; +end; + +if(strcmp(suffix, 'ras')) + %Write header + + fp = fopen(filename, 'wb'); + if(fp < 0) error(['Cannot open ' filename '.']), end + + [height, width] = size(image); + image = image - 1; + mapsize = size(map, 1)*size(map,2); + %fwrite(fp, ... + % [1504078485, width, height, 8, height*width, 1, 1, mapsize], ... + % 'long'); + + + zero_str = '00000000'; + + % MAGIC NUMBER: + + + fwrite(fp,89,'uchar'); + fwrite(fp,166,'uchar'); + fwrite(fp,106,'uchar'); + fwrite(fp,149,'uchar'); + + width_str = dec2hex(width); + width_str = [zero_str(1:8-length(width_str)) width_str]; + + for ii = 1:2:7, + fwrite(fp,hex2dec(width_str(ii:ii+1)),'uchar'); + end; + + + height_str = dec2hex(height); + height_str = [zero_str(1:8-length(height_str)) height_str]; + + for ii = 1:2:7, + fwrite(fp,hex2dec(height_str(ii:ii+1)),'uchar'); + end; + + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,8,'uchar'); + + ll = height*width; + ll_str = dec2hex(ll); + ll_str = [zero_str(1:8-length(ll_str)) ll_str]; + + for ii = 1:2:7, + fwrite(fp,hex2dec(ll_str(ii:ii+1)),'uchar'); + end; + + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,1,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,0,'uchar'); + fwrite(fp,~~mapsize,'uchar'); + + mapsize_str = dec2hex(mapsize); + mapsize_str = [zero_str(1:8-length(mapsize_str)) mapsize_str]; + + %keyboard; + + for ii = 1:2:7, + fwrite(fp,hex2dec(mapsize_str(ii:ii+1)),'uchar'); + end; + + + if mapsize ~= 0 + map = min(255, fix(255*map)); + fwrite(fp, map, 'uchar'); + end + if rem(width,2) == 1 + image = [image'; zeros(1, height)]'; + top = 255 * ones(size(image)); + fwrite(fp, min(top,image)', 'uchar'); + else + top = 255 * ones(size(image)); + fwrite(fp, min(top,image)', 'uchar'); + end + fclose(fp); +else + error('Image file name must end in either ''ras'' or ''rast''.'); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj2/TOOLBOX_calib.tar b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj2/TOOLBOX_calib.tar new file mode 100755 index 0000000..36d5de9 Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/calib_bouguetj2/TOOLBOX_calib.tar differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC.m b/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC.m new file mode 100755 index 0000000..d612780 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC.m @@ -0,0 +1,26 @@ +function [v,d] = Ncut_IC(I,nb_radius_IC); +% +% [v,d] = Ncut_IC(I,nb_radius_IC); +% + +if nargin<2, + nb_radius_IC = 5; +end + +I = I/max(I(:)); + +eg_par = [16,2, 21,3]; eg_th = 0; + +nv = 11; reg_fac = 0.01; + +[ex,ey,egx,egy,eg_par,eg_th,emag,ephase] = quadedgep(I,eg_par,eg_th); + + +nb_radius_IC= 10; +sample_rate = 0.2; +disp('setupW\n'); +[w_i,w_j] = cimgnbmap(size(I),nb_radius_IC,sample_rate); +w = affinityic(emag,ephase,w_i,w_j); +disp('computeNcut'); +[v,d] = ncut(w,nv,[],reg_fac); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m.m b/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m.m new file mode 100755 index 0000000..146acf9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m.m @@ -0,0 +1,42 @@ +function [v,d] = Ncut_IC_m(I,mask,nb_radius_IC,sig_IC); +% +% [v,d] = Ncut_IC_m(I,mask,nb_radius_IC,sig_IC); +% + +if nargin<2, + mask = ones(size(I)); +end + +if nargin<3, + nb_radius_IC = 10; +end + +if nargin<4, + sig_IC = 0.03; +end + +%%% normalize the image +I = I/max(I(:)); + +%%% edge detecting parameter, [num_ori, sig, win_size, enlongation factor] +eg_par = [6,2, 21,3]; eg_th = 0.01; + +%% number of eigenvectors+ regulization factors +nv = 10; reg_fac = 0.0; + +%% compute the edge response +[ex,ey,egx,egy,eg_par,eg_th,emag,ephase] = quadedgep(I,eg_par,eg_th); + +%%% setup Wij connection pattern +sample_rate = 0.1; +[w_i,w_j] = cimgnbmap(size(I),nb_radius_IC,sample_rate); + +%%% compute Wij with IC +emag = mask.*emag; +w = affinityic(emag,ephase,w_i,w_j,sig_IC); + +%show_dist_w(I,w); +%%% running Ncut +[v,d] = ncut(w,nv); + +v = reshape(v,size(I,1),size(I,2),size(v,2)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m2.m b/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m2.m new file mode 100755 index 0000000..9668b19 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/Ncut_IC_m2.m @@ -0,0 +1,51 @@ +function [v,d] = Ncut_IC_m(I,mask,nb_radius_IC,sig_IC); +% +% [v,d] = Ncut_IC_m(I,mask,nb_radius_IC,sig_IC); +% + +if nargin<2, + mask = ones(size(I)); +end + +if nargin<3, + nb_radius_IC = 10; +end + +if nargin<4, + sig_IC = 0.03; +end + +%%% normalize the image +I = I/max(I(:)); + +%%% edge detecting parameter, [num_ori, sig, win_size, enlongation factor] +eg_par = [6,2, 21,3]; eg_th = 0.01; + +%% number of eigenvectors+ regulization factors +nv = 10; reg_fac = 0.0; + +%% compute the edge response +[nr,nc,nb] = size(I); +emag = zeros(nr,nc); +ephase = zeros(nr,nc); +for j=1:nb, + [ex,ey,egx,egy,eg_par,eg_th,emag1,ephase1] = quadedgep(I(:,:,j),eg_par,eg_th); + mask = emag1>emag; + ephase = ephase+ mask.*ephase1; + emag = emag + mask.*emag1; +end + + +%%% setup Wij connection pattern +sample_rate = 0.1; +[w_i,w_j] = cimgnbmap(size(I),nb_radius_IC,sample_rate); + +%%% compute Wij with IC +emag = mask.*emag; +w = affinityic(emag,ephase,w_i,w_j,sig_IC); + +%show_dist_w(I,w); +%%% running Ncut +[v,d] = ncut(w,nv); + +v = reshape(v,size(I,1),size(I,2),size(v,2)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.c b/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.c new file mode 100755 index 0000000..e48762a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.c @@ -0,0 +1,186 @@ +/*================================================================ +* function w = affinityic(emag,ephase,pi,pj,sigma) +* Input: +* emag = edge strength at each pixel +* ephase = edge phase at each pixel +* [pi,pj] = index pair representation for MALTAB sparse matrices +* sigma = sigma for IC energy +* Output: +* w = affinity with IC at [pi,pj] +* + +% test sequence +f = synimg(10); +[i,j] = cimgnbmap(size(f),2); +[ex,ey,egx,egy] = quadedgep(f); +a = affinityic(ex,ey,egx,egy,i,j) +show_dist_w(f,a); + +* Jianbo Shi, Stella X. Yu, Nov 19, 2001. +*=================================================================*/ + +# include "mex.h" +# include "math.h" + +void mexFunction( + int nargout, + mxArray *out[], + int nargin, + const mxArray *in[] +) +{ + /* declare variables */ + int nr, nc, np, total; + int i, j, k, ix, iy, jx, jy, ii, jj, iip1, jjp1, iip2, jjp2, step; + double sigma, di, dj, a, z, maxori, phase1, phase2, slope; + int *ir, *jc; + unsigned long *pi, *pj; + double *emag, *ephase, *w; + + /* check argument */ + if (nargin<4) { + mexErrMsgTxt("Four input arguments required"); + } + if (nargout>1) { + mexErrMsgTxt("Too many output arguments"); + } + + /* get edgel information */ + nr = mxGetM(in[0]); + nc = mxGetN(in[0]); + if ( nr*nc ==0 || nr != mxGetM(in[1]) || nc != mxGetN(in[1]) ) { + mexErrMsgTxt("Edge magnitude and phase shall be of the same image size"); + } + emag = mxGetPr(in[0]); + ephase = mxGetPr(in[1]); + np = nr * nc; + + /* get new index pair */ + if (!mxIsUint32(in[2]) | !mxIsUint32(in[3])) { + mexErrMsgTxt("Index pair shall be of type UINT32"); + } + if (mxGetM(in[3]) * mxGetN(in[3]) != np + 1) { + mexErrMsgTxt("Wrong index representation"); + } + pi = mxGetData(in[2]); + pj = mxGetData(in[3]); + + /* create output */ + out[0] = mxCreateSparse(np,np,pj[np],mxREAL); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough memory for the output matrix"); + } + w = mxGetPr(out[0]); + ir = mxGetIr(out[0]); + jc = mxGetJc(out[0]); + + /* find my sigma */ + if (nargin<5) { + sigma = 0; + for (k=0; ksigma) { sigma = emag[k]; } + } + sigma = sigma / 10; + printf("sigma = %6.5f",sigma); + } else { + sigma = mxGetScalar(in[4]); + } + a = 1.0/ (sigma); + + /* computation */ + total = 0; + for (j=0; j= abs(dj)) { + slope = dj / di; + step = (iy>=jy) ? 1 : -1; + + iip1 = jy; + jjp1 = jx; + + + for (ii=0;ii maxori){ + maxori = z; + } + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + + /* sample in j direction */ + } else { + slope = di / dj; + step = (ix>=jx) ? 1: -1; + + jjp1 = jx; + iip1 = jy; + + + for (jj=0;jj maxori){ + maxori = z; + } + + } + + iip1 = iip2; + jjp1 = jjp2; + phase1 = phase2; + } + } + + maxori = 0.5 * maxori*a; + maxori = exp(-maxori*maxori); + } + ir[total] = i; + + w[total] = maxori + 0.005; + total = total + 1; + + } /* i */ + } /* j */ + + jc[np] = total; +} diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexa64 b/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexa64 new file mode 100755 index 0000000..e60b4d1 Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexa64 differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexglx b/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexglx new file mode 100755 index 0000000..5edb5d8 Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/common/affinityic.mexglx differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/anisodiff.m b/SD-VBS/common/toolbox/toolbox_basic/common/anisodiff.m new file mode 100755 index 0000000..b576d8f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/anisodiff.m @@ -0,0 +1,20 @@ +function [outimage] = anisodiff(inimage,iterations,K) +% [outimage] = anisodiff(inimage,iterations,K) +% Pietro's anisotropic diffusion routine + +lambda = 0.25; +outimage = inimage; [m,n] = size(inimage); + +rowC = [1:m]; rowN = [1 1:m-1]; rowS = [2:m m]; +colC = [1:n]; colE = [1 1:n-1]; colW = [2:n n]; + +for i=1:iterations, + deltaN = outimage(rowN,colC) - outimage(rowC,colC); + deltaE = outimage(rowC,colE) - outimage(rowC,colC); + + fluxN = deltaN .* exp( - ((1/K) * abs(deltaN)).^2 ); + fluxE = deltaE .* exp( - ((1/K) * abs(deltaE)).^2 ); + + outimage = outimage + lambda * (fluxN - fluxN(rowS,colC) + fluxE - fluxE(rowC,colW)); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/bin.m b/SD-VBS/common/toolbox/toolbox_basic/common/bin.m new file mode 100755 index 0000000..e2c3c90 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/bin.m @@ -0,0 +1,39 @@ +function [i, nnbins] = bin(x, dx, x0, x1); +% +% [i, nbins] = bin(x, dx, x0, x1); +% +% Returns the vector of indices, starting from 1, +% corresponding to the chosen bin size, dx, +% start x0 and end x1. If x1 is omitted, x1 = max(x) - dx/2. +% If x0 is omitted, x0 = min(x) + dx/2. If dx is omitted, the data +% are divided into 10 classes. Note that outliers are not removed. +% +% Tested under MatLab 4.2, 5.0, and 5.1. +% + +% 17.1.97, Oyvind.Breivik@gfi.uib.no. +% +% Oyvind Breivik +% Department of Geophysics +% University of Bergen +% NORWAY + +N = 10; % Default is 10 classes + +if nargin < 2 + dx = (max(x) - min(x))/N; +end +if nargin < 3 + x0 = min(x) + dx/2; +end +if nargin < 4 + x1 = max(x) - dx/2; +end +nbins = round((x1 - x0)/dx) + 1; +i = round((x - x0)/dx) + 1; +%in = (i >= 1) & (i <= nbins); % Indices are within range [1, nbins]. +%i = i(in); + +if nargout > 1 + nnbins = nbins; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/cimgnbmap.c b/SD-VBS/common/toolbox/toolbox_basic/common/cimgnbmap.c new file mode 100755 index 0000000..1595b68 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/cimgnbmap.c @@ -0,0 +1,189 @@ +/*================================================================ +* function [i,j] = cimgnbmap([nr,nc], nb_r, sample_rate) +* computes the neighbourhood index matrix of an image, +* with each neighbourhood sampled. +* Input: +* [nr,nc] = image size +* nb_r = neighbourhood radius, could be [r_i,r_j] for i,j +* sample_rate = sampling rate, default = 1 +* Output: +* [i,j] = each is a column vector, give indices of neighbour pairs +* UINT32 type +* i is of total length of valid elements, 0 for first row +* j is of length nr * nc + 1 +* +* See also: imgnbmap.c, id2cind.m +* +* Examples: +* [i,j] = imgnbmap(10, 20); % [10,10] are assumed +* +* Stella X. Yu, Nov 12, 2001. + +% test sequence: +nr = 15; +nc = 15; +nbr = 1; +[i,j] = cimgnbmap([nr,nc], nbr); +mask = csparse(i,j,ones(length(i),1),nr*nc); +show_dist_w(rand(nr,nc),mask) + +*=================================================================*/ + +# include "mex.h" +# include "math.h" + +void mexFunction( + int nargout, + mxArray *out[], + int nargin, + const mxArray *in[] +) +{ + /* declare variables */ + int nr, nc, np, nb, total; + double *dim, sample_rate; + int r_i, r_j, a1, a2, b1, b2, self, neighbor; + int i, j, k, s, t, nsamp, th_rand, no_sample; + unsigned long *p, *qi, *qj; + + /* check argument */ + if (nargin < 2) { + mexErrMsgTxt("Two input arguments required"); + } + if (nargout> 2) { + mexErrMsgTxt("Too many output arguments."); + } + + /* get image size */ + i = mxGetM(in[0]); + j = mxGetN(in[0]); + dim = mxGetData(in[0]); + nr = (int)dim[0]; + if (j>1 || i>1) { + nc = (int)dim[1]; + } else { + nc = nr; + } + np = nr * nc; + + /* get neighbourhood size */ + i = mxGetM(in[1]); + j = mxGetN(in[1]); + dim = mxGetData(in[1]); + r_i = (int)dim[0]; + if (j>1 || i>1) { + r_j = (int)dim[1]; + } else { + r_j = r_i; + } + if (r_i<0) { r_i = 0; } + if (r_j<0) { r_j = 0; } + + /* get sample rate */ + if (nargin==3) { + sample_rate = (mxGetM(in[2])==0) ? 1: mxGetScalar(in[2]); + } else { + sample_rate = 1; + } + /* prepare for random number generator */ + if (sample_rate<1) { + srand( (unsigned)time( NULL ) ); + th_rand = (int)ceil((double)RAND_MAX * sample_rate); + no_sample = 0; + } else { + sample_rate = 1; + th_rand = RAND_MAX; + no_sample = 1; + } + + /* figure out neighbourhood size */ + + nb = (r_i + r_i + 1) * (r_j + r_j + 1); + if (nb>np) { + nb = np; + } + nb = (int)ceil((double)nb * sample_rate); + + /* intermediate data structure */ + p = mxCalloc(np * (nb+1), sizeof(unsigned long)); + if (p==NULL) { + mexErrMsgTxt("Not enough space for my computation."); + } + + /* computation */ + total = 0; + for (j=0; j=nc) { b2 = nc-1; } + + /* i range */ + a1 = i - r_i; + if (a1<0) { a1 = 0; } + a2 = i + r_i; + if (a2>=nr) { a2 = nr-1; } + + /* number of more samples needed */ + nsamp = nb - p[self]; + + k = 0; + t = b1; + s = i + 1; + if (s>a2) { + s = a1; + t = t + 1; + } + while (ka2) { + s = a1; + t = t + 1; + } + } /* k */ + + total = total + p[self]; + } /* i */ + } /* j */ + + /* i, j */ + out[0] = mxCreateNumericMatrix(total, 1, mxUINT32_CLASS, mxREAL); + out[1] = mxCreateNumericMatrix(np+1, 1, mxUINT32_CLASS, mxREAL); + qi = mxGetData(out[0]); + qj = mxGetData(out[1]); + if (out[0]==NULL || out[1]==NULL) { + mexErrMsgTxt("Not enough space for the output matrix."); + } + + total = 0; + for (j=0; j 1) + xy0 = dx; + dx = DX; +end + +if nargin < 3 + dx = DX; +end + +if (nargin == 4 & length(dy) > 1) + xy0 = dy; + dy = dx; +end +if nargin < 4 + dy = dx; +end + +nxy = length(xy0); +xy(1:nxy) = xy0; + +if (isnan(xy(4))) + xy(4) = max(y); +end +if (isnan(xy(3))) + xy(3) = min(y); +end +if (isnan(xy(2))) + xy(2) = max(x); +end +if (isnan(xy(1))) + xy(1) = min(x); +end +x0 = xy(1); x1 = xy(2); y0 = xy(3); y1 = xy(4); + +if (dx < 0) + dx = (x1 - x0)/abs(dx); +end +if (dy < 0) + dy = (y1 - y0)/abs(dy); +end + +ix = bin(x, dx, x0, x1); +iy = bin(y, dy, y0, y1); % bin data in (x,y)-space + +xvec = x0:dx:x1; +yvec = y0:dy:y1; + +nx = length(xvec); +ny = length(yvec); + +inx = (ix >= 1) & (ix <= nx); +iny = (iy >= 1) & (iy <= ny); +in = (inx & iny); +ix = ix(in); iy = iy(in); +N = length(ix); % how many datapoints are left now? + +rho = zeros(length(xvec), length(yvec)) + eps; + +for i = 1:N + rho(ix(i), iy(i)) = rho(ix(i), iy(i)) + 1; +end + +rho = rho/(N*dx*dy); % Density is n per dx per dy + +rho = rho'; % Get in shape + +if nargout == 0 + pcolor(xvec, yvec, sqrt(rho)); shading interp; axis image; + colorbar + colormap jet + xlabel(inputname(1)) + ylabel(inputname(2)) + dum = size(rho'); + str = sprintf('%d data points, grid: %dx%d', N, dum(1)-1, dum(2)-1); + title(str); +end + +if nargout > 0 + rrho = rho; +end + +if nargout > 1 + xxvec = xvec; + yyvec = yvec; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/find_edge.m b/SD-VBS/common/toolbox/toolbox_basic/common/find_edge.m new file mode 100755 index 0000000..4299c29 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/find_edge.m @@ -0,0 +1,24 @@ +function [edgemap,mag,th] = find_edge(I,sig,mag_thld) +% +% [edgemap,mag,th] = find_edge(I,sig,mag_thld) +% + +if nargin<2, + sig = 1; +end + +if nargin<3, + mag_thld = 1/30; +end + +I = I/max(I(:)); + +ismax = 1;r = 1; + +[gx,gy] = grad(I,sig); +[th,mag] = cart2pol(gy,gx); + +g = cat(3,gy,gx); +edgemap = nonmaxsup(g,ismax,r); +edgemap = edgemap.*(mag>mag_thld); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/grad.m b/SD-VBS/common/toolbox/toolbox_basic/common/grad.m new file mode 100755 index 0000000..05fce39 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/grad.m @@ -0,0 +1,28 @@ +% gradient of an image +% coordinates (r, c) follow matrix convention; +% the gaussian is truncated at x = +- tail, and there are samples samples +% inbetween, where samples = hsamples * 2 + 1 + +function[gr,gc] = gradient(image, hsamples,cm) + +if nargin <3, + cm = 'same'; +end + +tail=4; +samples = hsamples * 2 + 1; + +x = linspace(-tail, tail, samples); +gauss = exp(-x.^2); +n = gauss * ones(samples,1); +gauss = gauss/n; + +gaussderiv = -x.*gauss; +n = -gaussderiv*linspace(1,samples,samples)'; +gaussderiv = gaussderiv/n; + +%gr = conv2(conv2(image, gaussderiv','valid'), gauss,'valid'); +%gc = conv2(conv2(image, gaussderiv,'valid'), gauss','valid'); + +gr = conv2(conv2(image, gaussderiv',cm), gauss,cm); +gc = conv2(conv2(image, gaussderiv,cm), gauss',cm); diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_even2.m b/SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_even2.m new file mode 100755 index 0000000..937f9bd --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_even2.m @@ -0,0 +1,45 @@ +function FB = make_filterbank(num_ori,filter_scales,wsz,enlong) +% +% F = make_filterbank(num_ori,num_scale,wsz) +% + +if nargin<4, + enlong = 3; +end + +enlong = 2*enlong + +% definine filterbank +%num_ori=6; +%num_scale=3; + +num_scale = length(filter_scales); + +M1=wsz; % size in pixels +M2=M1; + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +FB=zeros(M1,M2,num_ori,num_scale); + +% elongated filter set +counter = 1; + +for m=1:num_scale + for n=1:num_ori + % r=12 here is equivalent to Malik's r=3; + f=doog2(filter_scales(m),enlong,ori_offset+(n-1)*ori_incr,M1); + FB(:,:,n,m)=f; + end +end + +FB=reshape(FB,M1,M2,num_scale*num_ori); +total_num_filt=size(FB,3); + +for j=1:total_num_filt, + F = FB(:,:,j); + a = sum(sum(abs(F))); + FB(:,:,j) = FB(:,:,j)/a; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_odd2.m b/SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_odd2.m new file mode 100755 index 0000000..0059dca --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/make_filterbank_odd2.m @@ -0,0 +1,46 @@ +function FB = make_filterbank(num_ori,filter_scales,wsz,enlong) +% +% F = make_filterbank(num_ori,num_scale,wsz) +% + +if nargin<4, + enlong = 3; +end + +enlong = enlong*2; + +% definine filterbank +%num_ori=6; +%num_scale=3; + +num_scale = length(filter_scales); + +M1=wsz; % size in pixels +M2=M1; + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +FB=zeros(M1,M2,num_ori,num_scale); + + +% elongated filter set +counter = 1; + +for m=1:num_scale + for n=1:num_ori + % r=12 here is equivalent to Malik's r=3; + f=doog1(filter_scales(m),enlong,ori_offset+(n-1)*ori_incr,M1); + FB(:,:,n,m)=f; + end +end + +FB=reshape(FB,M1,M2,num_scale*num_ori); +total_num_filt=size(FB,3); + +for j=1:total_num_filt, + F = FB(:,:,j); + a = sum(sum(abs(F))); + FB(:,:,j) = FB(:,:,j)/a; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/max_supress2.m b/SD-VBS/common/toolbox/toolbox_basic/common/max_supress2.m new file mode 100755 index 0000000..05b5f11 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/max_supress2.m @@ -0,0 +1,59 @@ +function NMS = max_supress2(data,ismax); +% +% NMS = max_supress(data,ismax); +% +% data: [nr,nc,nfilter,nscale] +% of filter mag. map +% ismax: 1 local max, 0 local min +% + +[nr,nc,nfilter,nscale] = size(data); + +% set up the orthognal neighbourhood for each oriented filter +if nfilter == 6, + nbr_template=[1 1 1 0 -1 -1 + 0 1 1 1 1 1]; +else + nbr_template=[1 0 ; + 0 1]; +end + +%% for each scale, compute the dominate filter response +canny_dir_I = zeros(nr,nc,nscale); + +for m = 1:nscale, + [max_Ori_resp_I,Ori_sca_I] = max(data(:,:,:,m),[],3); + canny_dir_I(:,:,m) = Ori_sca_I; +end + +max_Ori_resp_small = max_Ori_resp_I(2:end-1,2:end-1); +canny_dir = canny_dir_I(2:end-1,2:end-1); + +%% + +NMS = zeros(nr,nc,nscale); + + +for m = 1:nscale, + + [x,y] = meshgrid(2:nc-1,2:nr-1); + xid = x(:)+nbr_template(2,canny_dir(:))'; + yid = y(:)+nbr_template(1,canny_dir(:))'; + id1 = (xid-1)*nr+yid; + + xid = x(:)-nbr_template(2,canny_dir(:))'; + yid = y(:)-nbr_template(1,canny_dir(:))'; + id2 = (xid-1)*nr+yid; + if ismax, + a = (max_Ori_resp_small(:)>max_Ori_resp_I(id1(:))) .* (max_Ori_resp_small(:)>max_Ori_resp_I(id2(:))); + NMS(2:end-1,2:end-1,m) = reshape(a,nr-2,nc-2); + NMS(:,:,m) = NMS(:,:,m).*max_Ori_resp_I; + else + a = (max_Ori_resp_small(:)0); + rr = wi.*(wi>0)-wr.*(wr<0); + + % decomposition + au = aa + aa'; + ad = aa - aa'; + ru = rr + rr'; + rd = rr - rr'; + + % construct equivalent matrices + x = sum(ru,2); + wr = au - ru + diag(x); + wi = ad + rd; + x = x + sum(au,2); + + % re-organize, add in offset and beta + z(:,j) = x + 2 * offset; + na = na + ( beta * (wr + offset) + sqrt(-1)* (2-beta) * wi ); + +end +z = sum(z,2); % diag(z) = single equivalent D + +% normalize +d = repmat(1./sqrt(z+eps),1,nc); +na = d.*na; +na = na.*d'; + +options.disp = 0; +%options.tol = 1e-10; +%options.maxit = 15; + +[v,s] = eigs(na,nv,sigma,options); +s = real(diag(s)); + +% project back to get the eigenvectors for the pair (a,d) +% a x = lambda d x +% na y = lambda y +% x = d^(-1/2) y + +v = v .* d(:,ones(nv,1)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/nonmaxsup.m b/SD-VBS/common/toolbox/toolbox_basic/common/nonmaxsup.m new file mode 100755 index 0000000..d12301c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/nonmaxsup.m @@ -0,0 +1,81 @@ +% function f = nonmaxsup(g,ismax,r) return extrema boolean map. +% Input: g = image, gradient image pair [x,y], or [x,y,g] in 3D matrix +% ismax (=1)/0 is for non maximum/minimum suppression +% r (=1) is the neighbourhood radius. +% Output: +% f = thinned extrema boolean map, where +% d (||gradient||) / d gradient = 0 + +% Stella X. Yu, 2000. + +function f = nonmaxsup(g,ismax,r) + +if nargin<2, + ismax = 1; +end + +if nargin<3, + r = 1; +end + +i = size(g,3); +if i==3, + x = g(:,:,1); + y = g(:,:,2); + g = g(:,:,3); +elseif i==2, + x = g(:,:,1); + y = g(:,:,2); + g = x.*x + y.*y; +else + [x,y] = gradient(g); +end + +% label angles into 4 directions +a = angle(x - sqrt(-1).*y); % [-pi,pi) +s = ceil((abs(a)+pi/8)./(pi/4)); +s(find(s==5)) = 1; +s(find(isnan(s))) = 1; + +% augment the image +[m,n] = size(g); +newm = m + r + r; +i = [g(:,1);g(:,end);g(1,:)';g(end,:)']; % image boundary +if ismax, + v = min(i) - 1; +else + v = max(i) + 1; +end +i = zeros(newm,r) + v; +j = zeros(r,n) + v; +newg = [i, [j; g; j;], i]; + +% k = index as the interior of the new image +i = [r+1:newm-r]'+ r*newm; +j = [0:n-1].*newm; +k = i(:,ones(1,n)) + j(ones(1,m),:); +k = k(:); + +% unit displacement vectors along gradient directions +d = [newm,newm-1,-1,-1-newm]'; % for 4 directions +d = d(s(:)); + +% non maximum suppression +f = ones(m*n,1); +g = g(:); +newd = 0; + +if ismax, + for i=1:r, + newd = newd + d; + f = f & (g>newg(k+newd)) & (g>newg(k-newd)); + end +else + for i=1:r, + newd = newd + d; + f = f & (g0); +have_value = [ j, 1-j ]; +j = 1; n_filter = have_value(j,:) * [par(j); def_par(j)]; +j = 2; n_scale = have_value(j,:) * [par(j); def_par(j)]; +j = 3; winsz = have_value(j,:) * [par(j); def_par(j)]; +j = 4; enlong = have_value(j,:) * [par(j); def_par(j)]; + +% always make filter size an odd number so that the results will not be skewed +j = winsz/2; +if not(j > fix(j) + 0.1), + winsz = winsz + 1; +end + +% filter the image with quadrature filters +FBo = make_filterbank_odd2(n_filter,n_scale,winsz,enlong); +FBe = make_filterbank_even2(n_filter,n_scale,winsz,enlong); +n = ceil(winsz/2); +f = [fliplr(I(:,2:n+1)), I, fliplr(I(:,c-n:c-1))]; +f = [flipud(f(2:n+1,:)); f; flipud(f(r-n:r-1,:))]; +FIo = fft_filt_2(f,FBo,1); +FIo = FIo(n+[1:r],n+[1:c],:); +FIe = fft_filt_2(f,FBe,1); +FIe = FIe(n+[1:r],n+[1:c],:); + +% compute the orientation energy and recover a smooth edge map +% pick up the maximum energy across scale and orientation +% even filter's output: as it is the second derivative, zero cross localize the edge +% odd filter's output: orientation +mag = sqrt(sum(FIo.^2,3)+sum(FIe.^2,3)); +mag_a = sqrt(FIo.^2+FIe.^2); +[tmp,max_id] = max(mag_a,[],3); +base_size = r * c; +id = [1:base_size]'; +mage = reshape(FIe(id+(max_id(:)-1)*base_size),[r,c]); +mage = (mage>0) - (mage<0); + +ori_incr=pi/n_filter; % to convert jshi's coords to conventional image xy +ori_offset=ori_incr/2; +theta = ori_offset+([1:n_filter]-1)*ori_incr; % orientation detectors +% [gx,gy] are image gradient in image xy coords, winner take all +mago = reshape(FIo(id+(max_id(:)-1)*base_size),[r,c]); +ori = theta(max_id); +ori = ori .* (mago>0) + (ori + pi).*(mago<0); +gy = mag .* cos(ori); +gx = -mag .* sin(ori); +g = cat(3,gx,gy); + +% phase map: edges are where the phase changes +mag_th = max(mag(:)) * threshold; +eg = (mag>mag_th); +h = eg & [(mage(2:r,:) ~= mage(1:r-1,:)); zeros(1,c)]; +v = eg & [(mage(:,2:c) ~= mage(:,1:c-1)), zeros(r,1)]; +[y,x] = find(h | v); +k = y + (x-1) * r; +h = h(k); +v = v(k); +y = y + h * 0.5; % i +x = x + v * 0.5; % j +t = h + v * r; +gx = g(k) + g(k+t); +k = k + (r * c); +gy = g(k) + g(k+t); + +% display +if 1, +%figure; showmask(I,mage<0); +figure(2); clf;showim(I,1); hold on; quiver(x,y,gx,gy); +%figure; showim(-I,1); hold on; quiver(i,j,ex,ey); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/readpcm.m b/SD-VBS/common/toolbox/toolbox_basic/common/readpcm.m new file mode 100755 index 0000000..ca736da --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/readpcm.m @@ -0,0 +1,12 @@ +function I = readpcm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d\n',2); +I = fscanf(fid,'%c',A(2)*A(1)); +I = I'; +I = str2num(I); +I = reshape(I,A(2),A(1))'; + + +fclose(fid); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/readpdm.m b/SD-VBS/common/toolbox/toolbox_basic/common/readpdm.m new file mode 100755 index 0000000..9a1068e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/readpdm.m @@ -0,0 +1,8 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); +I = fscanf(fid,'%d',[A(1),A(2)]); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/readpfm.m b/SD-VBS/common/toolbox/toolbox_basic/common/readpfm.m new file mode 100755 index 0000000..48ecd78 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/readpfm.m @@ -0,0 +1,10 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); +I = fscanf(fid,'%f',[A(1),A(2)]); + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/renormalize.m b/SD-VBS/common/toolbox/toolbox_basic/common/renormalize.m new file mode 100755 index 0000000..5d84724 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/renormalize.m @@ -0,0 +1,32 @@ +function W2 = renormalize(W,nstep) +% +% keep renormalizing until W is almost double +% stocastic +% + +if nargin<2, + nstep = 5; +end + +n_node = size(W,1); + +for j=1:nstep, + fprintf(','); + % normalize row + D = sum(W,2); + D = 1./(D+eps); + W = W.*D(:,ones(1,n_node)); + + % normlize column + D = sum(W,1); + D = 1./(D+eps); + W = W.*D(ones(n_node,1),:); +end +fprintf('\n'); + + D = sum(W,2); + D = 1./(D+eps); + W2 = W.*D(:,ones(1,n_node)); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/show_edge.m b/SD-VBS/common/toolbox/toolbox_basic/common/show_edge.m new file mode 100755 index 0000000..63b2f98 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/show_edge.m @@ -0,0 +1,11 @@ +function [id_i,id_j,ids] = show_edge(I,MI,thI); +% +% show_edge(I,MI,thI); +% + +[id_i,id_j,tmp] = find(MI); +ids = sub2ind(size(I),id_i,id_j); +clf;im(I);colormap(gray);hold on; +quiver(id_j,id_i,-sin(thI(ids)),cos(thI(ids)),0.5);hold off; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/common/spmtimesd.c b/SD-VBS/common/toolbox/toolbox_basic/common/spmtimesd.c new file mode 100755 index 0000000..a98dc0a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/common/spmtimesd.c @@ -0,0 +1,141 @@ +/*================================================================ +* spmtimesd.c +* This routine computes a sparse matrix times a diagonal matrix +* whose diagonal entries are stored in a full vector. +* +* Examples: +* spmtimesd(m,d,[]) = diag(d) * m, +* spmtimesd(m,[],d) = m * diag(d) +* spmtimesd(m,d1,d2) = diag(d1) * m * diag(d2) +* m could be complex, but d is assumed real +* +* Stella X. Yu's first MEX function, Nov 9, 2001. + +% test sequence: + +m = 1000; +n = 2000; +a=sparse(rand(m,n)); +d1 = rand(m,1); +d2 = rand(n,1); +tic; b=spmtimesd(a,d1,d2); toc +tic; bb = spdiags(d1,0,m,m) * a * spdiags(d2,0,n,n); toc +e = (bb-b); +max(abs(e(:))) + +*=================================================================*/ + +# include "mex.h" + +void mexFunction( + int nargout, + mxArray *out[], + int nargin, + const mxArray *in[] +) +{ + /* declare variables */ + int i, j, k, m, n, nzmax, cmplx, xm, yn; + int *pir, *pjc, *qir, *qjc; + double *x, *y, *pr, *pi, *qr, *qi; + + /* check argument */ + if (nargin != 3) { + mexErrMsgTxt("Three input arguments required"); + } + if (nargout>1) { + mexErrMsgTxt("Too many output arguments."); + } + if (!(mxIsSparse(in[0]))) { + mexErrMsgTxt("Input argument #1 must be of type sparse"); + } + if ( mxIsSparse(in[1]) || mxIsSparse(in[2]) ) { + mexErrMsgTxt("Input argument #2 & #3 must be of type full"); + } + + /* computation starts */ + m = mxGetM(in[0]); + n = mxGetN(in[0]); + pr = mxGetPr(in[0]); + pi = mxGetPi(in[0]); + pir = mxGetIr(in[0]); + pjc = mxGetJc(in[0]); + + i = mxGetM(in[1]); + j = mxGetN(in[1]); + xm = ((i>j)? i: j); + + i = mxGetM(in[2]); + j = mxGetN(in[2]); + yn = ((i>j)? i: j); + + if ( xm>0 && xm != m) { + mexErrMsgTxt("Row multiplication dimension mismatch."); + } + if ( yn>0 && yn != n) { + mexErrMsgTxt("Column multiplication dimension mismatch."); + } + + + nzmax = mxGetNzmax(in[0]); + cmplx = (pi==NULL ? 0 : 1); + out[0] = mxCreateSparse(m,n,nzmax,cmplx); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough space for the output matrix."); + } + + qr = mxGetPr(out[0]); + qi = mxGetPi(out[0]); + qir = mxGetIr(out[0]); + qjc = mxGetJc(out[0]); + + /* left multiplication */ + x = mxGetPr(in[1]); + if (yn==0) { + for (j=0; j2 + image(RGB) + axis('image') +%end diff --git a/SD-VBS/common/toolbox/toolbox_basic/disp/showmaskb.m b/SD-VBS/common/toolbox/toolbox_basic/disp/showmaskb.m new file mode 100755 index 0000000..1f67ba2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/disp/showmaskb.m @@ -0,0 +1,20 @@ +function RGB = showmask(V,M,M2,display_flag); +% showmask(V,M); +% +% M is a nonneg. mask + +V=V-min(V(:)); +V=V/max(V(:)); +V=.25+0.75*V; %brighten things up a bit + +M=M-min(M(:)); +M=M/max(M(:)); + +H=0.6*M2+0*M; +S=min(1,M2+M); +RGB=hsv2rgb(H,S,V); + +%if nargin>2 + image(RGB) + axis('image') +%end diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/construct_w.m b/SD-VBS/common/toolbox/toolbox_basic/fact/construct_w.m new file mode 100755 index 0000000..372b4b6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/construct_w.m @@ -0,0 +1,25 @@ +function W = construct_w(centers,Ds,img_center,indexes,frames) +% +% function W = construct_w(centers,Ds,img_center,indexes,frames) +% optional: frames +% + + +points = length(indexes); +if (nargin == 4), + frames = 0.5*size(centers,2); +end + +W = zeros(2*frames,points); + +center_x = img_center(1); +center_y = img_center(2); + +for j=1:frames, + % x is centers(:,2*j-1) + % y is centers(:,2*j) + % d is Ds(:,2*j-1) + W(j,:) = (centers(indexes,2*j-1) -center_x)'./Ds(indexes,2*j-1)'; + W(j+frames,:) = (centers(indexes,2*j) -center_y)'./Ds(indexes,2*j-1)'; + % W(j+2*frames,:) = ones(1,points)./Ds(indexes,2*j-1)'; +end \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/construct_w2.m b/SD-VBS/common/toolbox/toolbox_basic/fact/construct_w2.m new file mode 100755 index 0000000..b2939b7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/construct_w2.m @@ -0,0 +1,25 @@ +function W = construct_w2(centers,Ds,img_center,indexes,frames) +% +% function W = construct_w2(centers,Ds,img_center,indexes,frames) +% optional: frames +% + + +points = length(indexes); +if (nargin == 4), + frames = 0.5*size(centers,2); +end + +W = zeros(3*frames,points); + +center_x = img_center(1); +center_y = img_center(2); + +for j=1:frames, + % x is centers(:,2*j-1) + % y is centers(:,2*j) + % d is Ds(:,2*j-1) + W(j,:) = (centers(indexes,2*j-1) -center_x)'./Ds(indexes,2*j-1)'; + W(j+frames,:) = (centers(indexes,2*j) -center_y)'./Ds(indexes,2*j-1)'; + W(j+2*frames,:) = ones(1,points)./Ds(indexes,2*j-1)'; +end \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/factor.m b/SD-VBS/common/toolbox/toolbox_basic/fact/factor.m new file mode 100755 index 0000000..635c29a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/factor.m @@ -0,0 +1,50 @@ +% +% Usage: +% +% [R,t,S] = factor(W) +% +% Function to factor a matrix of input data (W) into the camera +% rotation matrix (R), translation (t), and the shape matrix (S). +% Three-dimensional version. Failure of normalization results in +% empty R and S. + +function [R,t,S] = factor(W) + +pts = size(W,2); +t = W*ones(pts,1)/pts; +W = W - t*ones(1,pts); + +% Use SVD to factor W. + [a,b,c] = svd(W,0); + +smallb = b(1:3,1:3); % Since W is rank 3, b has only three meaningful values +sqrtb = sqrt(smallb); +Rhat = a(:,1:3) * sqrtb; +Shat = sqrtb * c(:,1:3)'; + +G = findG(Rhat); + +if size(G,1) == 0, +R = []; +S = []; +else + R = Rhat*G; + S = inv(G)*Shat; + + % rotation matrix that aligns the reference frame with the first camera + F = size(R,1)/2; + R1 = R(1,:); + R1 = R1/norm(R1); + R2 = R(F+1,:); + R2 = R2/norm(R2); + R3 = cross(R1,R2); + R3 = R3/norm(R3); + P = [R1; R2; R3]; + P = P'; + + R = R*P; + S = inv(P)*S; +end + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/factor_test.m b/SD-VBS/common/toolbox/toolbox_basic/fact/factor_test.m new file mode 100755 index 0000000..12ceb95 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/factor_test.m @@ -0,0 +1,52 @@ +% +% Usage: +% +% [R,t,S] = factor(W) +% +% Function to factor a matrix of input data (W) into the camera +% rotation matrix (R), translation (t), and the shape matrix (S). +% Three-dimensional version. Failure of normalization results in +% empty R and S. + +function [R,t,S,C,b] = factor(W) + +pts = size(W,2); +t = W*ones(pts,1)/pts; +W = W - t*ones(1,pts); + +% Use SVD to factor W. + [a,b,c] = svd(W,0); + +figure(3);plot(diag(b)) + +smallb = b(1:3,1:3); % Since W is rank 3, b has only three meaningful values +sqrtb = sqrt(smallb); +Rhat = a(:,1:3) * sqrtb; +Shat = sqrtb * c(:,1:3)'; + +[G,C] = findg1(Rhat); + +if size(G,1) == 0, +R = []; +S = []; +else + R = Rhat*G; + S = inv(G)*Shat; + + % rotation matrix that aligns the reference frame with the first camera + F = size(R,1)/2; + R1 = R(1,:); + R1 = R1/norm(R1); + R2 = R(F+1,:); + R2 = R2/norm(R2); + R3 = cross(R1,R2); + R3 = R3/norm(R3); + P = [R1; R2; R3]; + P = P'; + + R = R*P; + S = inv(P)*S; +end + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/factor_test2.m b/SD-VBS/common/toolbox/toolbox_basic/fact/factor_test2.m new file mode 100755 index 0000000..3520122 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/factor_test2.m @@ -0,0 +1,52 @@ +% +% Usage: +% +% [R,t,S] = factor(W) +% +% Function to factor a matrix of input data (W) into the camera +% rotation matrix (R), translation (t), and the shape matrix (S). +% Three-dimensional version. Failure of normalization results in +% empty R and S. + +function [R,t,S,C,b] = factor(W) + +pts = size(W,2); +t = W*ones(pts,1)/pts; +W = W - t*ones(1,pts); + +% Use SVD to factor W. + [a,b,c] = svd(W,0); + +figure(3);plot(diag(b)) + +smallb = b(1:3,1:3); % Since W is rank 3, b has only three meaningful values +sqrtb = sqrt(smallb); +Rhat = a(:,1:3) * sqrtb; +Shat = sqrtb * c(:,1:3)'; + +[G,C] = findg2(Rhat); + +if size(G,1) == 0, +R = []; +S = []; +else + R = Rhat*G; + S = inv(G)*Shat; + + % rotation matrix that aligns the reference frame with the first camera + F = size(R,1)/2; + R1 = R(1,:); + R1 = R1/norm(R1); + R2 = R(F+1,:); + R2 = R2/norm(R2); + R3 = cross(R1,R2); + R3 = R3/norm(R3); + P = [R1; R2; R3]; + P = P'; + + R = R*P; + S = inv(P)*S; +end + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/factorizaion.tar b/SD-VBS/common/toolbox/toolbox_basic/fact/factorizaion.tar new file mode 100755 index 0000000..133cdff Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/fact/factorizaion.tar differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/findG.m b/SD-VBS/common/toolbox/toolbox_basic/fact/findG.m new file mode 100755 index 0000000..9a6bd73 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/findG.m @@ -0,0 +1,48 @@ +function G = find3G(Rhat) + +% number of frames +F = size(Rhat,1)/2; + +% Build matrix Q such that Q * v = [1,...,1,0,...,0] where v is a six +% element vector containg all six distinct elements of the Matrix C + +clear Q +for f = 1:F, + g = f + F; + h = g + F; + Q(f,:) = zt(Rhat(f,:), Rhat(f,:)); + Q(g,:) = zt(Rhat(g,:), Rhat(g,:)); + Q(h,:) = zt(Rhat(f,:), Rhat(g,:)); +end + +% Solve for v +rhs = [ones(2*F,1); zeros(F,1)]; +v = Q \ rhs; + +% C is a symmetric 3x3 matrix such that C = G * transpose(G) +C(1,1) = v(1); +C(1,2) = v(2); +C(1,3) = v(3); +C(2,2) = v(4); +C(2,3) = v(5); +C(3,3) = v(6); +C(2,1) = C(1,2); +C(3,1) = C(1,3); +C(3,2) = C(2,3); + +e = eig(C); +disp(e) + +if (any(e<= 0)), + G = []; +else + G = sqrtm(C); +end + +%neg = 0; +%if e(1) <= 0, neg = 1; end +%if e(2) <= 0, neg = 1; end +%if e(3) <= 0, neg = 1; end +%if neg == 1, G = []; +%else G = sqrtm(C); +%end diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/findg1.m b/SD-VBS/common/toolbox/toolbox_basic/fact/findg1.m new file mode 100755 index 0000000..f14ecc6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/findg1.m @@ -0,0 +1,49 @@ +function [G,C] = find3G(Rhat) + +% number of frames +F = size(Rhat,1)/2; + +% Build matrix Q such that Q * v = [1,...,1,0,...,0] where v is a six +% element vector containg all six distinct elements of the Matrix C + +clear Q +for f = 1:F, + g = f + F; + h = g + F; + Q(f,:) = zt(Rhat(f,:), Rhat(f,:)); + Q(g,:) = zt(Rhat(g,:), Rhat(g,:)); + Q(h,:) = zt(Rhat(f,:), Rhat(g,:)); +end + +% Solve for v +rhs = [ones(2*F,1); zeros(F,1)]; +v = Q \ rhs; + +% C is a symmetric 3x3 matrix such that C = G * transpose(G) +C(1,1) = v(1); +C(1,2) = v(2); +C(1,3) = v(3); +C(2,2) = v(4); +C(2,3) = v(5); +C(3,3) = v(6); +C(2,1) = C(1,2); +C(3,1) = C(1,3); +C(3,2) = C(2,3); + +e = eig(C); +disp(e) + +if (any(e<= 0)), + C = C -2*min(e)*eye(3); + G = sqrtm(C); +else + G = sqrtm(C); +end + +%neg = 0; +%if e(1) <= 0, neg = 1; end +%if e(2) <= 0, neg = 1; end +%if e(3) <= 0, neg = 1; end +%if neg == 1, G = []; +%else G = sqrtm(C); +%end diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/findg2.m b/SD-VBS/common/toolbox/toolbox_basic/fact/findg2.m new file mode 100755 index 0000000..5a84b86 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/findg2.m @@ -0,0 +1,56 @@ +function [G,C] = find3G(Rhat) + +% number of frames +F = size(Rhat,1)/3; + +% Build matrix Q such that Q * v = [1,...,1,0,...,0] where v is a six +% element vector containg all six distinct elements of the Matrix C + +clear Q +for f = 1:F, + g = f + F; + h = g + F; + j = h + F; + k = j + F; + l = k + F; + Q(f,:) = zt(Rhat(f,:), Rhat(f,:)); + Q(g,:) = zt(Rhat(g,:), Rhat(g,:)); + Q(h,:) = zt(Rhat(h,:), Rhat(h,:)); + Q(j,:) = zt(Rhat(f,:), Rhat(g,:)); + Q(k,:) = zt(Rhat(f,:), Rhat(h,:)); + Q(l,:) = zt(Rhat(g,:), Rhat(h,:)); +end + +% Solve for v +rhs = [ones(3*F,1); zeros(3*F,1)]; +v = Q \ rhs; + +% C is a symmetric 3x3 matrix such that C = G * transpose(G) +C(1,1) = v(1); +C(1,2) = v(2); +C(1,3) = v(3); +C(2,2) = v(4); +C(2,3) = v(5); +C(3,3) = v(6); +C(2,1) = C(1,2); +C(3,1) = C(1,3); +C(3,2) = C(2,3); + +e = eig(C); +disp(e) + + +if (any(e<= 0)), + C = C -2*min(e)*eye(3); + G = sqrtm(C); +else + G = sqrtm(C); +end + +%neg = 0; +%if e(1) <= 0, neg = 1; end +%if e(2) <= 0, neg = 1; end +%if e(3) <= 0, neg = 1; end +%if neg == 1, G = []; +%else G = sqrtm(C); +%end diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/hotel.mat b/SD-VBS/common/toolbox/toolbox_basic/fact/hotel.mat new file mode 100755 index 0000000..61ea6c8 Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/fact/hotel.mat differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/show_3dpoints.m b/SD-VBS/common/toolbox/toolbox_basic/fact/show_3dpoints.m new file mode 100755 index 0000000..b6edfd5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/show_3dpoints.m @@ -0,0 +1,22 @@ +function show_3dpoints(S) + + +for j=1:size(S,2), + x = S(1,j); + y = S(2,j); + z = S(3,j); + plot3(x,y,z,'*'); + hold on; + plot3([x,0],[y,0],[z,0],'r'); +% plot3([x,x],[y,y],[z,0],'r'); +% plot3([x,0],[y,y],[z,z],'r'); plot3([x,x],[y,0],[z,z],'r'); + text(x,y,z,int2str(j)) +% plot3(x,y,0,'co'); +end + +grid on +xlabel('x'); +ylabel('y'); +zlabel('z'); + +hold off \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/show_S.m b/SD-VBS/common/toolbox/toolbox_basic/fact/show_S.m new file mode 100755 index 0000000..5828696 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/show_S.m @@ -0,0 +1,17 @@ +function show_S(S,fig) + +if (nargin == 1), + figure(1); +else + figure(fig); +end + +num_points = size(S,2); + +subplot(1,2,1); plot(S(1,:),S(3,:),'cx'); axis('equal');axis('square');hold on +subplot(1,2,2); plot(S(2,:),S(3,:),'cx'); axis('equal');axis('square');hold on + +for j=1:num_points, + subplot(1,2,1);text(S(1,j),S(3,j),int2str(j));hold off + subplot(1,2,2);text(S(2,j),S(3,j),int2str(j));hold off +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/show_t.m b/SD-VBS/common/toolbox/toolbox_basic/fact/show_t.m new file mode 100755 index 0000000..b475c76 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/show_t.m @@ -0,0 +1,10 @@ +function show_t(t) + +frames = 0.5*length(t); + +ts = reshape(t,frames,2); + +plot(ts(:,1),ts(:,2)); +hold on +plot(ts(:,1),ts(:,2),'rx'); +hold off; diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/show_t3.m b/SD-VBS/common/toolbox/toolbox_basic/fact/show_t3.m new file mode 100755 index 0000000..2766061 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/show_t3.m @@ -0,0 +1,10 @@ +function show_t(t) + +frames = length(t)/3; + +ts = reshape(t,frames,3); + +plot3(ts(:,1),ts(:,2),ts(:,3)); +hold on +plot3(ts(:,1),ts(:,2),ts(:,3),'rx'); +hold off; diff --git a/SD-VBS/common/toolbox/toolbox_basic/fact/zt.m b/SD-VBS/common/toolbox/toolbox_basic/fact/zt.m new file mode 100755 index 0000000..3f88d21 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/fact/zt.m @@ -0,0 +1,6 @@ +% the z' operator described in the paper (returns a row vector) + +function v = zt(a, b) + +v = [ a(1)*b(1), a(1)*b(2)+a(2)*b(1), a(1)*b(3)+a(3)*b(1), ... + a(2)*b(2), a(2)*b(3)+a(3)*b(2), a(3)*b(3) ]; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/91048.jpg b/SD-VBS/common/toolbox/toolbox_basic/filter/91048.jpg new file mode 100755 index 0000000..6b2313b Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/filter/91048.jpg differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/bar2d.m b/SD-VBS/common/toolbox/toolbox_basic/filter/bar2d.m new file mode 100755 index 0000000..76fa819 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/bar2d.m @@ -0,0 +1,16 @@ +function kernel = bar2d(sigx,sigy,siz,angle) + +X = -siz:.1:siz; +G = exp(-0.5*X.^2/sigx^2); + +DGG = (1/sigy^2) * ((X/sigy).^2-1) .* exp(- (X/sigy).^2/2); +%DGG = (X.^2/(sqrt(2*pi)*sigy^5) - 1/(sqrt(2*pi)*sigy^2)) .* ... +% exp(-0.5*X.^2/sigy^2); + +K = G'*DGG; +K = rotate_J(angle,K); + +K = imresize(K,0.1); +K = K-mean(mean(K)); + +kernel = K; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/barrot.m b/SD-VBS/common/toolbox/toolbox_basic/filter/barrot.m new file mode 100755 index 0000000..bd7676e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/barrot.m @@ -0,0 +1,22 @@ +img1 = gifread('color010.gif'); +img1 = img1(220:350,1:200); +img2 = gifread('color-avg.gif'); +img2 = img2(220:350,1:200); + +sigx = 3; +sigy = 3; +siz = 8; +angles = 0:19:179; + +[Imag1,Iangle1] = brute_force_angle(img1,sigx,sigy,siz,angles); +[Imag2,Iangle2] = brute_force_angle(img2,sigx,sigy,siz,angles); + +tresh = max(max(Imag1))*0.1 +D = angle_diff(Imag1,Iangle1,Imag2,Iangle2,tresh); +subplot(2,2,1); imagesc(Iangle1*180/pi); colorbar; +subplot(2,2,2); imagesc(Iangle2*180/pi); colorbar; +subplot(2,2,3); imagesc(D*180/pi); colorbar; +subplot(2,2,4); imagesc(Imag1); colorbar; +colormap(jet) + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/bars.m b/SD-VBS/common/toolbox/toolbox_basic/filter/bars.m new file mode 100755 index 0000000..77eccea --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/bars.m @@ -0,0 +1,39 @@ +function [filt] = bars(X,Y,ks); +%FIL1 the first filter to use has the following specifications: +% +% real part: 2nd derivative of gaussian along Y +% normal gaussian along X +% This filter is elongated along the X direction +% imag part: hilbert transform of the real part +% +% [filt] = fil1(X,Y,ks); +% X,Y : index matrix obtained by meshgrid +% ks : kernel size +% filt : the output kernel +% + +%% +%% (c) Thomas Leung +%% California Institute of Technology +%% Feb 27, 1994. +%% + +if(nargin == 2) + ks = 17; +end + +sigmay = 2.4 * ks / 17; +sigmax = 3 * sigmay; + +fxr = exp(-(X/sigmax).^2/2); +fyr = (1/sigmay^2) * ((Y/sigmay).^2-1) .* exp(- (Y/sigmay).^2/2); +nrm = 1/(sigmax*sigmay*2*pi); + +% real part of filter +fr = nrm * fxr .* fyr; + +% imag part of filter +filt = hilbert(fr); + + +return; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/clip_image.m b/SD-VBS/common/toolbox/toolbox_basic/filter/clip_image.m new file mode 100755 index 0000000..bf7b50c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/clip_image.m @@ -0,0 +1,6 @@ +function J = clip_image(I,w) + +[size_y,size_x] = size(I); + +J = I(w+1:size_y-w,w+1:size_x-w); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/compute_J_simple.m b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_J_simple.m new file mode 100755 index 0000000..e790601 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_J_simple.m @@ -0,0 +1,50 @@ +function J = compute_J(I,A,D,base) +%% function J = compute_J(I,A,D) +% + +if nargin == 3, + base = -1; +end + +[size_y,size_x] = size(I); +[center_x,center_y] = find_center(size_x,size_y); + +add_x = round(size_x*0.45); +add_y = round(size_y*0.45); +big_I = base*ones(size_y+2*add_y,size_x+2*add_x); + +big_I(add_y+1:add_y+size_y,add_x+1:add_x+size_x) = I; + +center_x = add_x+ center_x; +center_y = add_y+ center_y; +[size_y,size_x] = size(big_I); + +%a = angle * pi/180; +%A = [cos(a),-sin(a);sin(a),cos(a)]; + +[XX,YY] = meshgrid(1:size_x,1:size_y); + +x = reshape(XX,size_x*size_y,1); +y = reshape(YY,size_x*size_y,1); +index(:,1) = x-center_x; + +%index(:,2) = (size_y+1) - y; +index(:,2) = y-center_y; + +position_new = A*index'; +position_new(1,:) = position_new(1,:)+D(1)+center_x; +position_new(2,:) = position_new(2,:)+D(2)+center_y; +%position_new(2,:) = (size_y+1) - position_new(2,:); + +position_new_x = reshape(position_new(1,:),size_y,size_x); +position_new_y = reshape(position_new(2,:),size_y,size_x); + +J = m_interp4(big_I,position_new_x,position_new_y); + + +[size_y,size_x] = size(I); +J = J(add_y+1:add_y+size_y,add_x+1:add_x+size_x); + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/compute_angle.m b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_angle.m new file mode 100755 index 0000000..7a995af --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_angle.m @@ -0,0 +1,18 @@ +function [angle,mag,c2,c3] = compute_angle(I) + +[g,ga,gb,gc] = compute_g2(I,0); +[h,ha,hb,hc,hd] = compute_h2(I,0); + +c2 = 0.5*(ga.^2 - gc.^2) + 0.46875*(ha.^2 - hd.^2) +... + 0.28125*(hb.^2 - hc.^2) + 0.1875*(ha.*hc - hb.*hd); + +c3 = -ga.*gb - gb.*gc - 0.9375*(hc.*hd + ha.*hb) -... + 1.6875*hb.*hc - 0.1875*ha.*hd; + +[angle,mag] = cart2pol(-c2,-c3); + +%angle = angle/2+pi/2; +%angle = (angle>pi).*(angle-2*pi) + (angle<=pi).*angle; + +angle = angle/2; +mag = sqrt(mag); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/compute_filter_fft.m b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_filter_fft.m new file mode 100755 index 0000000..359c6ba --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_filter_fft.m @@ -0,0 +1,84 @@ +function [filter_output,filters] = compute_filter_fft(I,sig,r,sz,num_ori); +% +% +% + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +as = ori_offset:ori_incr:180+ori_offset-ori_incr; + +filter_output = []; +filters = []; + +wsz = 2*round(sz(end)) + 1; +M1 = wsz;M2 = wsz; + +%%%%% prepare FFT of image %%%%%%%%%%%%% + +[N1,N2]=size(I); +tmp=zeros(size(I)+[M1-1 M2-1]); +tmp(1:N1,1:N2)=I; +IF=fft2(tmp); + + +%%%%%%%%%% filtering stage %%%%%%%%%%% +if size(sig,2)== 1, + + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + + g = g - mean(reshape(g,prod(size(g)),1)); + + g = g/sum(sum(abs(g))); + + filters(:,:,j) = g; + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = IF.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + + filter_output(:,:,j) = Ig; + end +else + + % there are multiple scales + sigs = sig; + szs = sz; + for k = 1:size(sigs,2), + sig = sigs(k); + sz = szs(end); + fprintf('%d',k); + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + g = g - mean(reshape(g,prod(size(g)),1)); + g = g/sum(sum(abs(g))); + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = IF.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + %c = conv2(I,g,'same'); + + filter_output(:,:,j,k) = Ig; + filters(:,:,j,k) = g; + end + + + end + +end + +fprintf('\n'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/compute_g2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_g2.m new file mode 100755 index 0000000..ac27d00 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_g2.m @@ -0,0 +1,23 @@ +function [g,ga,gb,gc] = compute_g2(I,angle) + +if (nargin == 1), + angle = 0; +end + +f1 = [0.0094 0.1148 0.3964 -0.0601 -0.9213 -0.0601 0.3964 0.1148 0.0094]; +f2 = [0.0008 0.0176 0.166 0.6383 1.0 0.6383 0.166 0.0176 0.0008]; +f3 = [-0.0028 -0.048 -0.302 -0.5806 0 0.5806 0.302 0.048 0.0028]; + +%ga = conv2(conv2(I,f2,'same'),f1','same'); +%gb = conv2(conv2(I,f3,'same'),f3','same'); +%gc = conv2(conv2(I,f1,'same'),f2','same'); + +ga = conv2(conv2(I,f1,'same'),f2','same'); +gb = conv2(conv2(I,f3,'same'),f3','same'); +gc = conv2(conv2(I,f2,'same'),f1','same'); + +ka = cos(angle)^2; +kb = -2*cos(angle)*sin(angle); +kc = sin(angle)^2; + +g = ka*ga + kb*gb + kc*gc; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/compute_h2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_h2.m new file mode 100755 index 0000000..e4cdcfb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_h2.m @@ -0,0 +1,27 @@ +function [h,ha,hb,hc,hd] = compute_h2(I,angle) + +if (nargin == 1), + angle = 0; +end + +f1 = [0.0098 0.0618 -0.0998 -0.7551 0 0.7551 0.0998 -0.0618 -0.0098]; +f2 = [ 0.0008 0.0176 0.166 0.6383 1 0.6383 0.166 0.0176 0.0008]; +f3 = -[-0.002 -0.0354 -0.2225 -0.4277 0 0.4277 0.2225 0.0354 0.002]; +f4 = [0.0048 0.0566 0.1695 -0.1889 -0.7349 -0.1889 0.1695 0.0566 0.0048]; + +%ha = conv2(conv2(I,f2,'same'),f1','same'); +%hb = conv2(conv2(I,f3,'same'),f4','same'); +%hc = conv2(conv2(I,f4,'same'),f3','same'); +%hd = conv2(conv2(I,f1,'same'),f2','same'); + +ha = conv2(conv2(I,f1,'same'),f2','same'); +hb = conv2(conv2(I,f4,'same'),f3','same'); +hc = conv2(conv2(I,f3,'same'),f4','same'); +hd = conv2(conv2(I,f2,'same'),f1','same'); + +ka = cos(angle)^3; +kb = -3*cos(angle)^2*sin(angle); +kc = 3*cos(angle)*sin(angle)^2; +kd = -sin(angle)^3; + +h = ka*ha + kb*hb + kc*hc + kd*hd; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/compute_ofilter_fft.m b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_ofilter_fft.m new file mode 100755 index 0000000..41989ed --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/compute_ofilter_fft.m @@ -0,0 +1,88 @@ +function [filter_output,filters] = compute_odd_filter_fft(I,sig,r,sz,num_ori); +% +% computes the filter response of I to the set of odd symmetric filters +% +% sig = scale(sigma) for the filters +% r = enlongation factor +% sz = the radius of the filters +% num_ori = number of orientations +% + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +as = ori_offset:ori_incr:180+ori_offset-ori_incr; + +filter_output = zeros(size(I,1),size(I,2),num_ori,length(sig)); +filters = []; + +wsz = 2*round(sz(end)) + 1; +M1 = wsz;M2 = wsz; + +%%%%% prepare FFT of image %%%%%%%%%%%%% + +[N1,N2]=size(I); +tmp=zeros(size(I)+[M1-1 M2-1]); +tmp(1:N1,1:N2)=I; +IF=fft2(tmp); + + +%%%%%%%%%% filtering stage %%%%%%%%%%% +if size(sig,2)== 1, + + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mk_odd_filter(sig,r,angle,round(sz)); + + g = g - mean(reshape(g,prod(size(g)),1)); + + g = g/sum(sum(abs(g))); + + filters(:,:,j,1) = g; + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = IF.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + + filter_output(:,:,j,1) = Ig; + end +else + + % there are multiple scales + sigs = sig; + szs = sz; + for k = 1:size(sigs,2), + sig = sigs(k); + sz = szs(end); + fprintf('%d',k); + + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mk_odd_filter(sig,r,angle,round(sz)); + g = g - mean(reshape(g,prod(size(g)),1)); + g = g/sum(sum(abs(g))); + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = IF.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + %c = conv2(I,g,'same'); + + filter_output(:,:,j,k) = Ig; + filters(:,:,j,k) = g; + end + end + +end + +fprintf('\n'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/dgauss.m b/SD-VBS/common/toolbox/toolbox_basic/filter/dgauss.m new file mode 100755 index 0000000..207e781 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/dgauss.m @@ -0,0 +1,16 @@ +function dg = dgauss(sig) +% first derivative of N(sig) +% cutoff after 1% of max + + i = 1; + max = 0; + dgi = max; + dg = [dgi]; + while dgi >= 0.01*max + dgi = i / (sqrt(2*pi) * sig^3) * exp(-0.5*i^2/sig^2); + dg = [dgi dg -dgi]; + i = i + 1; + if dgi > max + max = dgi; + end + end; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/dog1.m b/SD-VBS/common/toolbox/toolbox_basic/filter/dog1.m new file mode 100755 index 0000000..e9f64e6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/dog1.m @@ -0,0 +1,28 @@ +function G=dog1(sig,N); +% G=dog1(sig,N); + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +sigi=0.71*sig; +sigo=1.14*sig; +Ci=diag([sigi,sigi]); +Co=diag([sigo,sigo]); + +X=[x(:) y(:)]; + +Ga=gaussian(X,[0 0]',Ci); +Ga=reshape(Ga,N,N); +Gb=gaussian(X,[0 0]',Co); +Gb=reshape(Gb,N,N); + +a=1; +b=-1; + +G = a*Ga + b*Gb; + +G=G-mean(G(:)); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/dog2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/dog2.m new file mode 100755 index 0000000..8446198 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/dog2.m @@ -0,0 +1,31 @@ +function G=dog2(sig,N); +% G=dog2(sig,N); + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +sigi=0.62*sig; +sigo=1.6*sig; +C=diag([sig,sig]); +Ci=diag([sigi,sigi]); +Co=diag([sigo,sigo]); + +X=[x(:) y(:)]; + +Ga=gaussian(X,[0 0]',Ci); +Ga=reshape(Ga,N,N); +Gb=gaussian(X,[0 0]',C); +Gb=reshape(Gb,N,N); +Gc=gaussian(X,[0 0]',Co); +Gc=reshape(Gc,N,N); + +a=-1; +b=2; +c=-1; + +G = a*Ga + b*Gb + c*Gc; + +G=G-mean(G(:)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/doog1.m b/SD-VBS/common/toolbox/toolbox_basic/filter/doog1.m new file mode 100755 index 0000000..dd8e87b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/doog1.m @@ -0,0 +1,32 @@ +function H=doog1(sig,r,th,N); +% H=doog1(sig,r,th,N); + + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +phi=pi*th/180; +sigy=sig; +sigx=r*sig; +R=[cos(phi) -sin(phi); sin(phi) cos(phi)]; +C=R*diag([sigx,sigy])*R'; + +X=[x(:) y(:)]; + +Gb=gaussian(X,[0 0]',C); +Gb=reshape(Gb,N,N); + +m=R*[0 sig]'; + +a=1; +b=-1; + +% make odd-symmetric filter +Ga=gaussian(X,m/2,C); +Ga=reshape(Ga,N,N); +Gb=rot90(Ga,2); +H=a*Ga+b*Gb; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/doog2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/doog2.m new file mode 100755 index 0000000..a0511cb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/doog2.m @@ -0,0 +1,38 @@ +function G=doog2(sig,r,th,N); +% G=doog2(sig,r,th,N); +% Make difference of offset gaussians kernel +% theta is in degrees +% (see Malik & Perona, J. Opt. Soc. Amer., 1990) +% +% Example: +% >> imagesc(doog2(1,12,0,64,1)) +% >> colormap(gray) + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +phi=pi*th/180; +sigy=sig; +sigx=r*sig; +R=[cos(phi) -sin(phi); sin(phi) cos(phi)]; +C=R*diag([sigx,sigy])*R'; + +X=[x(:) y(:)]; + +Gb=gaussian(X,[0 0]',C); +Gb=reshape(Gb,N,N); + +m=R*[0 sig]'; +Ga=gaussian(X,m,C); +Ga=reshape(Ga,N,N); +Gc=rot90(Ga,2); + +a=-1; +b=2; +c=-1; + +G = a*Ga + b*Gb + c*Gc; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt.m b/SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt.m new file mode 100755 index 0000000..25fa5f9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt.m @@ -0,0 +1,82 @@ +% script for fft-based filtering + +% set up filterbank +make_filterbank + +% prepare FFT of image for filtering +[N1,N2]=size(V); +I=zeros(size(V)+[M1-1 M2-1]); +I(1:N1,1:N2)=V; +IF=fft2(I); +FI=zeros(N1,N2,total_num_filt); + +% apply filters +for n=1:total_num_filt + disp(n) + f=rot90(FB(:,:,n),2); + fF=fft2(f,N1+M1-1,N2+M2-1); + IfF=IF.*fF; + If=real(ifft2(IfF)); + If=If(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + FI(:,:,n)=If; +% im(If) +% drawnow +end + +%%%% end of filtering part; the remainder is for reconstruction & analysis +break + + +% use pseudoinverse to reconstruct image from filter projections +fbv=reshape(FB,M1*M2,total_num_filt)'; +fbi=pinv(fbv); + +% find principal components +T=reshape(FI,N1*N2,total_num_filt)'; +C=T*T'; +[U,S,junk]=svd(C); +s=diag(S); + +% synthesize using some eigenvectors +synth=fbi*U; +k=ceil(sqrt(total_num_filt)); +for n=1:total_num_filt + subplot(k,k,n) + im(reshape(synth(:,n),M1,M2)); + title(num2str(s(n))) + drawnow +end + +% synthesize at a point by clicking on coordinates +figure(1) +im(V) +[x,y]=ginput(1); +x=round(x); +y=round(y); +u=squeeze(FI(y,x,:)); +synth=fbi*u; +synth=reshape(synth,M1,M2); +figure(2) +subplot(1,2,1) +im(V) +axis([x-M2/2 x+M2/2 y-M1/2 y+M1/2]) +subplot(1,2,2) +im(synth) +title(num2str(max(synth(:)))); + +figure(3) +plot(u,'o-') + +% show pseudoinverse filterbank +if 0 +k=ceil(sqrt(total_num_filt)); +for n=1:total_num_filt + subplot(k,k,n) + im(reshape(fbi(:,n),M1,M2)); + axis('off') + drawnow +end +end + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt_2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt_2.m new file mode 100755 index 0000000..9c84e96 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/fft_filt_2.m @@ -0,0 +1,29 @@ +function FI=fft_filt_2(V,FB,sf); +% FI=fft_filt_2(V,FB,sf); +% fft-based filtering +% requires image to be called "V" +% and filters to be in FB +% sf is the subsampling factor +% +% FI is the result + +[M1,M2,N3]=size(FB); +% prepare FFT of image for filtering +[N1,N2]=size(V); +I=zeros(size(V)+[M1-1 M2-1]); +I(1:N1,1:N2)=V; +N1s=length(1:sf:N1); +N2s=length(1:sf:N2); +IF=fft2(I); +FI=zeros(N1s,N2s,N3); + +% apply filters +for n=1:N3; + f=rot90(FB(:,:,n),2); + fF=fft2(f,N1+M1-1,N2+M2-1); + IfF=IF.*fF; + If=real(ifft2(IfF)); + If=If(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + FI(:,:,n)=If(1:sf:N1,1:sf:N2); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/filter_bank_jshi.tar b/SD-VBS/common/toolbox/toolbox_basic/filter/filter_bank_jshi.tar new file mode 100755 index 0000000..b43b49c Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/filter/filter_bank_jshi.tar differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/gauss.m b/SD-VBS/common/toolbox/toolbox_basic/filter/gauss.m new file mode 100755 index 0000000..f0403ed --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/gauss.m @@ -0,0 +1,16 @@ +function dg = gauss(sig) +% first derivative of N(sig) +% cutoff after 1% of max + + i = 1; + max = 0; + dgi = max; + dg = [1/ (sqrt(2*pi) * sig) ]; + while dgi >= 0.01*max + dgi = 1/ (sqrt(2*pi) * sig) * exp(-0.5*i^2/sig^2); + dg = [dgi dg dgi]; + i = i + 1; + if dgi > max + max = dgi; + end + end; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/gaussian.m b/SD-VBS/common/toolbox/toolbox_basic/filter/gaussian.m new file mode 100755 index 0000000..509b129 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/gaussian.m @@ -0,0 +1,31 @@ +function p=gaussian(x,m,C); +% p=gaussian(x,m,C); +% +% Evaluate the multi-variate density with mean vector m and covariance +% matrix C for the input vector x. +% +% p=gaussian(X,m,C); +% +% Vectorized version: Here X is a matrix of column vectors, and p is +% a vector of probabilities for each vector. + +d=length(m); + +if size(x,1)~=d + x=x'; +end +N=size(x,2); + +detC = det(C); +if rcond(C)threshold); + + + + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/get_diff_free.m b/SD-VBS/common/toolbox/toolbox_basic/filter/get_diff_free.m new file mode 100755 index 0000000..f020fab --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/get_diff_free.m @@ -0,0 +1,8 @@ +function [diff,corr,mag,angle] = get_diff_free(I,J) + +[angle,mag,c2,c3] = compute_angle(I); +[angleJ,magJ] = compute_angle(J); + +corr = cos(angle).*cos(angleJ) + sin(angle).*sin(angleJ); +threshold = 0.075*max(max(mag(5:size(mag,1)-5,5:size(mag,2)-5))); +diff = (abs(corr)<0.9).*(mag>threshold); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/grad1.m b/SD-VBS/common/toolbox/toolbox_basic/filter/grad1.m new file mode 100755 index 0000000..eca34db --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/grad1.m @@ -0,0 +1,11 @@ +function [gx,gy] = grad2(I,ratio) +% +% + +kern = dgauss(ratio);kern = kern/sum(abs(kern)); +gkern = gauss(ratio);gkern = gkern/sum(abs(kern)); + +gx = conv2(I,kern,'same'); +gx = conv2(gx,gkern','same'); + +gy = conv2(conv2(I,kern','same'),gkern,'same'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/grad2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/grad2.m new file mode 100755 index 0000000..ea4ec0e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/grad2.m @@ -0,0 +1,11 @@ +function [gx,gy] = grad2(I,ratio) +% +% + +ddgauss = gradient(dgauss(ratio));ddgauss = ddgauss/sum(abs(ddgauss)); +gkern = gauss(ratio); gkern = gkern/sum(abs(gkern)); + +gx = conv2(I,ddgauss,'same'); +gx = conv2(gx,gkern','same'); + +gy = conv2(conv2(I,ddgauss','same'),gkern,'same'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/m_interp4.m b/SD-VBS/common/toolbox/toolbox_basic/filter/m_interp4.m new file mode 100755 index 0000000..314f140 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/m_interp4.m @@ -0,0 +1,49 @@ +function [F,mask] = m_interp4(z,s,t) +%INTERP4 2-D bilinear data interpolation. +% ZI = INTERP4(Z,XI,YI) assumes X = 1:N and Y = 1:M, where +% [M,N] = SIZE(Z). +% +% Copyright (c) 1984-93 by The MathWorks, Inc. +% Clay M. Thompson 4-26-91, revised 7-3-91, 3-22-93 by CMT. +% +% modified to + + +[nrows,ncols] = size(z); + + +if any(size(z)<[3 3]), error('Z must be at least 3-by-3.'); end +if size(s)~=size(t), error('XI and YI must be the same size.'); end + +% Check for out of range values of s and set to 1 +sout = find((s<1)|(s>ncols)); +if length(sout)>0, s(sout) = ones(size(sout)); end + +% Check for out of range values of t and set to 1 +tout = find((t<1)|(t>nrows)); +if length(tout)>0, t(tout) = ones(size(tout)); end + +% Matrix element indexing +ndx = floor(t)+floor(s-1)*nrows; + +% Compute intepolation parameters, check for boundary value. +d = find(s==ncols); +s(:) = (s - floor(s)); +if length(d)>0, s(d) = s(d)+1; ndx(d) = ndx(d)-nrows; end + +% Compute intepolation parameters, check for boundary value. +d = find(t==nrows); +t(:) = (t - floor(t)); +if length(d)>0, t(d) = t(d)+1; ndx(d) = ndx(d)-1; end +d = []; + +% Now interpolate, reuse u and v to save memory. +F = ( z(ndx).*(1-t) + z(ndx+1).*t ).*(1-s) + ... + ( z(ndx+nrows).*(1-t) + z(ndx+(nrows+1)).*t ).*s; + +mask = ones(size(z)); + +% Now set out of range values to zeros. +if length(sout)>0, F(sout) = zeros(size(sout));mask(sout)=zeros(size(sout));end +if length(tout)>0, F(tout) = zeros(size(tout));mask(tout)=zeros(size(tout));end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank.m b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank.m new file mode 100755 index 0000000..2ff15d2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank.m @@ -0,0 +1,63 @@ +function FB = make_filterbank(num_ori,num_scale,wsz) +% +% F = make_filterbank(num_ori,num_scale,wsz) +% + + +% definine filterbank +%num_ori=6; +%num_scale=3; + +M1=wsz; % size in pixels +M2=M1; + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +FBdoog1=zeros(M1,M2,num_scale,num_ori); +FBdoog2=zeros(M1,M2,num_scale,num_ori); +FBdog1=zeros(M1,M2,num_scale); +FBdog2=zeros(M1,M2,num_scale); + +% elongated filter set +counter = 1; +filter_scale = 1.0; +filter_scale_step = sqrt(2); + +for m=1:num_scale + f=dog1(filter_scale,M1); + FBdog1(:,:,m)=f; + f=dog2(filter_scale,M1); + FBdog2(:,:,m)=f; + counter=counter+1; + for n=1:num_ori + % r=12 here is equivalent to Malik's r=3; + f=doog2(filter_scale,6,ori_offset+(n-1)*ori_incr,M1); + FBdoog2(:,:,m,n)=f; + f=doog1(filter_scale,6,ori_offset+(n-1)*ori_incr,M1); + FBdoog1(:,:,m,n)=f; + end + filter_scale = filter_scale * filter_scale_step; +end + +FB=cat(3,3*FBdog1,4.15*FBdog2,2*reshape(FBdoog1,M1,M2,num_scale*num_ori),2*reshape(FBdoog2,M1,M2,num_scale*num_ori)); +total_num_filt=size(FB,3); + +nb = size(FB,3); +for j=1:nb, + F = FB(:,:,j); + a = sum(sum(abs(F))); + FB(:,:,j) = FB(:,:,j)/a; +end + + +if 0 +k=ceil(sqrt(total_num_filt)); +for n=1:total_num_filt + subplot(k,k,n) + im(FB(:,:,n)); + axis('off') + drawnow +end +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_23.m b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_23.m new file mode 100755 index 0000000..f9fcaa9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_23.m @@ -0,0 +1,40 @@ +function [FB,M1,M2,N3]=make_filterbank_23; +% multi-scale even and odd filters + +M1=31; % size in pixels +M2=M1; +num_ori=6; +num_scales=3; +num_phases=2; +N3=num_ori*num_scales*num_phases; +FB=zeros(M1,M2,N3); + +counter=1; + +for m=1:num_scales + for n=1:num_ori + [F1,F2]=quadpair(sqrt(2)^m,3,180*(n-1)/num_ori,M1); + FB(:,:,counter)=F1; + counter=counter+1; + FB(:,:,counter)=F2; + counter=counter+1; + end +end + +FB=cat(3,FB,dog2(1,M1),dog2(sqrt(2),M1),dog2(2,M1),dog2(2*sqrt(2),M1)); + +N3=size(FB,3); + +% stuff for visualizing spectra of filters: +if 0 +FBF=zeros(size(FB)); + +for n=1:36 + FBF(:,:,n)=abs(fftshift(fft2(FB(:,:,n)))); +end + +montage2(FBF) + +im(sum(FBF,3)) + +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_even.m b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_even.m new file mode 100755 index 0000000..8c8d802 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_even.m @@ -0,0 +1,40 @@ +function FB = make_filterbank(num_ori,filter_scales,wsz) +% +% F = make_filterbank(num_ori,num_scale,wsz) +% + + +% definine filterbank +%num_ori=6; +%num_scale=3; + +num_scale = length(filter_scales); + +M1=wsz; % size in pixels +M2=M1; + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +FB=zeros(M1,M2,num_ori,num_scale); + +% elongated filter set +counter = 1; + +for m=1:num_scale + for n=1:num_ori + % r=12 here is equivalent to Malik's r=3; + f=doog2(filter_scales(m),6,ori_offset+(n-1)*ori_incr,M1); + FB(:,:,n,m)=f; + end +end + +FB=reshape(FB,M1,M2,num_scale*num_ori); +total_num_filt=size(FB,3); + +for j=1:total_num_filt, + F = FB(:,:,j); + a = sum(sum(abs(F))); + FB(:,:,j) = FB(:,:,j)/a; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_odd.m b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_odd.m new file mode 100755 index 0000000..8103598 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/make_filterbank_odd.m @@ -0,0 +1,41 @@ +function FB = make_filterbank(num_ori,filter_scales,wsz) +% +% F = make_filterbank(num_ori,num_scale,wsz) +% + + +% definine filterbank +%num_ori=6; +%num_scale=3; + +num_scale = length(filter_scales); + +M1=wsz; % size in pixels +M2=M1; + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +FB=zeros(M1,M2,num_ori,num_scale); + + +% elongated filter set +counter = 1; + +for m=1:num_scale + for n=1:num_ori + % r=12 here is equivalent to Malik's r=3; + f=doog1(filter_scales(m),6,ori_offset+(n-1)*ori_incr,M1); + FB(:,:,n,m)=f; + end +end + +FB=reshape(FB,M1,M2,num_scale*num_ori); +total_num_filt=size(FB,3); + +for j=1:total_num_filt, + F = FB(:,:,j); + a = sum(sum(abs(F))); + FB(:,:,j) = FB(:,:,j)/a; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mdoog2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mdoog2.m new file mode 100755 index 0000000..25bc2f9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mdoog2.m @@ -0,0 +1,36 @@ +function G=doog2(sig,r,th,N); +% [G,H]=doog2(sig,r,th,N); +% Make difference of offset gaussians kernel +% theta is in degrees +% (see Malik & Perona, J. Opt. Soc. Amer., 1990) +% + + +[x,y]=meshgrid(-N:N,-N:N); + +a=-1; +b=2; +c=-1; + +ya=sig; +yc=-ya; +yb=0; +sigy=sig; +sigx=r*sig; + +Ga=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-ya)/sigy).^2)); +Gb=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-yb)/sigy).^2)); +Gc=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-yc)/sigy).^2)); + +Go = a*Ga + b*Gb + c*Gc; +%Ho = imag(hilbert(Go)); +G = Go; + +G = mimrotate(Go,th,'bilinear','crop'); + +G = G-mean(reshape(G,prod(size(G)),1)); + +G = G/sum(sum(abs(G))); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mimrotate.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mimrotate.m new file mode 100755 index 0000000..7dd31a2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mimrotate.m @@ -0,0 +1,119 @@ +function bout = imrotate(arg1,arg2,arg3,arg4) +%IMROTATE Rotate image. +% B = IMROTATE(A,ANGLE,'method') rotates the image A by ANGLE +% degrees. The image returned B will, in general, be larger +% than A. Invalid values on the periphery are set to one +% for indexed images or zero for all other image types. Possible +% interpolation methods are 'nearest','bilinear' or 'bicubic'. +% 'bilinear' is the default for intensity images, otherwise +% 'nearest' is used if no method is given. +% +% B = IMROTATE(A,ANGLE,'crop') or IMROTATE(A,ANGLE,'method','crop') +% crops B to be the same size as A. +% +% Without output arguments, IMROTATE(...) displays the rotated +% image in the current axis. +% +% See also IMRESIZE, IMCROP, ROT90. + +% Clay M. Thompson 8-4-92 +% Copyright (c) 1992 by The MathWorks, Inc. +% $Revision: 1.14 $ $Date: 1993/09/01 21:27:38 $ + +if nargin<2, error('Requires at least two input parameters.'); end +if nargin<3, + if isgray(arg1), caseid = 'bil'; else caseid = 'nea'; end + docrop = 0; +elseif nargin==3, + if isstr(arg3), + method = [lower(arg3),' ']; % Protect against short method + caseid = method(1:3); + if caseid(1)=='c', % Crop string + if isgray(arg1), caseid = 'bil'; else caseid = 'nea'; end + docrop = 1; + else + docrop = 0; + end + else + error('''METHOD'' must be a string of at least three characters.'); + end +else + if isstr(arg3), + method = [lower(arg3),' ']; % Protect against short method + caseid = method(1:3); + else + error('''METHOD'' must be a string of at least three characters.'); + end + docrop = 1; +end + +% Catch and speed up 90 degree rotations +if rem(arg2,90)==0 & nargin<4, + phi = rem(arg2,360); + if phi==90, + b = rot90(arg1); + elseif phi==180, + b = rot90(arg1,2); + elseif phi==270, + b = rot90(arg1,-1); + else + b = arg1; + end + if nargout==0, imshow(b), else bout = b; end + return +end + +phi = arg2*pi/180; % Convert to radians + +% Rotation matrix +T = [cos(phi) -sin(phi); sin(phi) cos(phi)]; + +% Coordinates from center of A +[m,n] = size(arg1); +if ~docrop, % Determine limits for rotated image + siz = ceil(max(abs([(n-1)/2 -(m-1)/2;(n-1)/2 (m-1)/2]*T))/2)*2; + uu = -siz(1):siz(1); vv = -siz(2):siz(2); +else % Cropped image + uu = (1:n)-(n+1)/2; vv = (1:m)-(m+1)/2; +end +nu = length(uu); nv = length(vv); + +blk = bestblk([nv nu]); +nblks = floor([nv nu]./blk); nrem = [nv nu] - nblks.*blk; +mblocks = nblks(1); nblocks = nblks(2); +mb = blk(1); nb = blk(2); + +rows = 1:blk(1); b = zeros(nv,nu); +for i=0:mblocks, + if i==mblocks, rows = (1:nrem(1)); end + for j=0:nblocks, + if j==0, cols = 1:blk(2); elseif j==nblocks, cols=(1:nrem(2)); end + if ~isempty(rows) & ~isempty(cols) + [u,v] = meshgrid(uu(j*nb+cols),vv(i*mb+rows)); + % Rotate points + uv = [u(:) v(:)]*T'; % Rotate points + u(:) = uv(:,1)+(n+1)/2; v(:) = uv(:,2)+(m+1)/2; + if caseid(1)=='n', % Nearest neighbor interpolation + b(i*mb+rows,j*nb+cols) = interp6(arg1,u,v); + elseif all(caseid=='bil'), % Bilinear interpolation + b(i*mb+rows,j*nb+cols) = interp2(arg1,u,v,'linear'); + elseif all(caseid=='bic'), % Bicubic interpolation + b(i*mb+rows,j*nb+cols) = interp5(arg1,u,v); + else + error(['Unknown interpolation method: ',method]); + end + end + end +end + +d = find(isnan(b)); +if length(d)>0, + if isind(arg1), b(d) = ones(size(d)); else b(d) = zeros(size(d)); end +end + +if nargout==0, + imshow(b), return +end +bout = b; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mk_odd_filter.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mk_odd_filter.m new file mode 100755 index 0000000..43ec7d7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mk_odd_filter.m @@ -0,0 +1,36 @@ +function G=doog2(sig,r,th,N); +% [G,H]=doog2(sig,r,th,N); +% Make difference of offset gaussians kernel +% theta is in degrees +% (see Malik & Perona, J. Opt. Soc. Amer., 1990) +% + + +[x,y]=meshgrid(-N:N,-N:N); + +a=-1; +b=2; +c=-1; + +ya=sig; +yc=-ya; +yb=0; +sigy=sig; +sigx=r*sig; + +Ga=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-ya)/sigy).^2)); +Gb=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-yb)/sigy).^2)); +Gc=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-yc)/sigy).^2)); + +Go = a*Ga + b*Gb + c*Gc; +Ho = imag(hilbert(Go)); +%G = Ho; + +G = mimrotate(Ho,th,'bilinear','crop'); + +G = G-mean(reshape(G,prod(size(G)),1)); + +G = G/sum(sum(abs(G))); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mkdog1.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdog1.m new file mode 100755 index 0000000..f1225cc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdog1.m @@ -0,0 +1,20 @@ +function dog1 = mkdog1(sigma_base,size_w) +% +% function dog1 = mkdog1(sigma_base,size_w) +% +% + +%scale_base = 1; +scale_base = 3; + +a = scale_base; +c = -1*scale_base; + +sigma_a = 0.71*sigma_base; +sigma_c = 1.14*sigma_base; + +dog1 = a*mkg(0,0,sigma_a,sigma_a,size_w) +... + c*mkg(0,0,sigma_c,sigma_c,size_w); + +dog1 = dog1-mean(reshape(dog1,prod(size(dog1)),1)); +dog1 = dog1/sum(sum(abs(dog1))); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mkdog2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdog2.m new file mode 100755 index 0000000..a78824a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdog2.m @@ -0,0 +1,22 @@ +function dog2 = mkdog2(sigma_base,size_w) +% +% function dog2 = mkdog2(sigma_base,size_w) +% +% + +%scale_base = 1.224; +scale_base = 4.15; + +a = scale_base; +b = -2*scale_base; +c = scale_base; + +sigma_a = 0.62*sigma_base; +sigma_b = sigma_base; +sigma_c = 1.6*sigma_base; + +dog2 = a*mkg(0,0,sigma_a,sigma_a,size_w) +... + b*mkg(0,0,sigma_b,sigma_b,size_w) +... + c*mkg(0,0,sigma_c,sigma_c,size_w); + +%dog2 = 255*5.1745*dog2; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mkdoog2.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdoog2.m new file mode 100755 index 0000000..5db2877 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdoog2.m @@ -0,0 +1,30 @@ +function doog2 = mkdoog2(sigma_w,r,theta,size_w) +% +% function doog2 = mkdoog2(sigma_w,r,theta,size_w) +% +% + +%scale_base = 2.8814; +scale_base = 2; + +a = -1*scale_base; +b = 2*scale_base; +c = -1*scale_base; + +sigma_x = r*sigma_w; +sigma_y = sigma_w; + +ya = sigma_w; +yc = -sigma_w; +yb = 0; + +doog2 = a*mkg(0,ya,sigma_x,sigma_y,size_w) +... + b*mkg(0,yb,sigma_x,sigma_y,size_w) +... + c*mkg(0,yc,sigma_x,sigma_y,size_w); + +%doog2 = 255*5.1745*doog2; + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mkdoogs.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdoogs.m new file mode 100755 index 0000000..e5796bc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mkdoogs.m @@ -0,0 +1,15 @@ +function [doogs,index] = mkdoogs(sigma_w,r,theta,theta_to,size_w) +% function doogs = mkdoogs(sigma_w,r,theta,theta_to,size_w) +% + +doogs = []; + +angle_start = theta*pi/180; +angle_end = theta_to*pi/180; +step = pi/180; + +index = 1; +for k=angle_start:step:angle_end, + doogs = [doogs,mkdoog2(sigma_w,r,k,size_w)]; + index = index +1; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/mkg.m b/SD-VBS/common/toolbox/toolbox_basic/filter/mkg.m new file mode 100755 index 0000000..1fb1f7e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/mkg.m @@ -0,0 +1,9 @@ +function g= mkgaussian(xo,yo,sigma_x,sigma_y,size_w) +% +% function G = mkgaussian(xo,yo,sigma_x,sigma_y,size_w) +% + +size_wh = round(0.5*size_w); +[x,y] = meshgrid([-size_wh:1:size_wh],[-size_wh:1:size_wh]); +g = 1/(2*pi*sigma_x*sigma_y)*(exp(-( ((x-xo)/sigma_x).^2 + ((y-yo)/sigma_y).^2))); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/quadpair.m b/SD-VBS/common/toolbox/toolbox_basic/filter/quadpair.m new file mode 100755 index 0000000..96c2a22 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/quadpair.m @@ -0,0 +1,20 @@ +function [F1,F2]=quadpair(sig,lam,th,N); +% [F1,F2]=quadpair(sig,lam,th,N); +% +% For Thomas' ECCV98 filters, use sig=sqrt(2), lam=4. + +%N=31; +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + + +F1=(4*(y.^2)/(sig^4)-2/(sig^2)).*exp(-(y.^2)/(sig^2)-(x.^2)/(lam^2*sig^2)); +F2=imag(hilbert(F1)); + +F1=imrotate(F1,th,'bil','crop'); +F2=imrotate(F2,th,'bil','crop'); + +F1=F1-mean(F1(:)); +F2=F2-mean(F2(:)); + +F1=F1/norm(F1(:),1); +F2=F2/norm(F2(:),1); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/smooth.m b/SD-VBS/common/toolbox/toolbox_basic/filter/smooth.m new file mode 100755 index 0000000..5ef7579 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/smooth.m @@ -0,0 +1,24 @@ +% smooth an image +% coordinates (r, c) follow matrix convention; +% the gaussian is truncated at x = +- tail, and there are samples samples +% inbetween, where samples = hsamples * 2 + 1 + +function g = smooth(image, hsamples) + +tail=4; +samples = hsamples * 2 + 1; + +x = linspace(-tail, tail, samples); +gauss = exp(-x.^2); +%s = sum(gauss)/length(x);gauss = gauss-s; +gauss = gauss/sum(abs(gauss)); + +n = gauss * ones(samples,1); +gauss = gauss/n; + + +g = conv2(conv2(image, gauss,'same'), gauss','same'); +%g = conv2(conv2(image, gauss,'valid'), gauss','valid'); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter/softkmean.m b/SD-VBS/common/toolbox/toolbox_basic/filter/softkmean.m new file mode 100755 index 0000000..b9b4feb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter/softkmean.m @@ -0,0 +1,56 @@ +function [cluster,var,mix,membership,lG] = softkmeans(data,k,cluster0) + +[n,D] = size(data); +var = 1.0; +var0 = ones(k,1)*var; minvar = 0.0001; +mix0 = ones(k,1)/k; minmix = 0.0001; + +k = size(var0,1); +[n,D] = size(data); + +lGG = []; +ma = -1e20; +in = 0; + +if (nargin == 2), + max_data = max(data); + min_data = min(data); + %step = (max_data-min_data)/(k+1); + %cluster0 = [1:k]'*step+min_data; + mag = ones(k,1)*(max_data-min_data); + base = ones(k,1)*min_data; + cluster0 = rand(k,D).*mag + base; +end + +%cluster0 +for t = 1:3, + %rndindx = round(rand(1,k)*(n-3))+2; + %cluster0 = (data(rndindx,:)+data(rndindx+1,:)+data(rndindx-1,:))/2; + [cluster,var,mix,membership,lG] = softmeans(cluster0,var0,minvar,mix0,minmix,data); + eval(sprintf('mix_var_cluster_%d = [mix,var,cluster];',t)); + eval(sprintf('lG_%d = lG;',t)); + if ma 1, Hj = sum(Hj')'; end + H(:,j) = exp(Hj /(-2*var_p(j)))/(sqrt(var_p(j))^D); + end + H = H.*(ones(n,1)*mix_p'); + new_lg = sum(log(sum(H')/(sqrt(2*pi)^D))); + lG = [lG, new_lg]; + if new_lg == old_lg, break; end; old_lg = new_lg; + H = H./(sum(H')'*ones(1,k)); % normalize + + % M-Step: + + if minmix > 0, + mix_p = sum(H); mix_p = mix_p/sum(mix_p); mix_p = mix_p'; + for j = 1:k, if mix_p(j) 0, + for j = 1:k, + varj = (data-(ones(n,1)*cluster_p(j,:))).^2; + if D > 1, varj = sum(varj')'; end + var_p(j) = sum(H(:,j).*varj)/(D*sum(H(:,j))); + if var_p(j)'); +end + +fprintf('\n'); + +s = 1./sqrt(d); + +for j=1:nv, + v(:,j) = v(:,j)*s(j); +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/backproj_outer_chank2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/backproj_outer_chank2.m new file mode 100755 index 0000000..084c150 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/backproj_outer_chank2.m @@ -0,0 +1,36 @@ +function v = backproj_outer_chank(fvs,u,d,chank_size) +% +% given the eigenvecs of the hist.bin. features +% computes the back projection on the eigenvects +% + +[nv,np] = size(fvs); +[nbins,nv] = size(u); + +n_chanks = ceil(np/chank_size); + +v = ones(np,nv); + +for j=1:n_chanks, + fprintf('<'); + + cm = sprintf('load st_%d',j); + eval(cm); + fprintf(sprintf('%d',n_chanks-j)); + + ms = mean(fh'); + fh = fh - ms'*ones(1,size(fh,2)); + + v((j-1)*chank_size+1:min(np,j*chank_size),:) = fh'*u; + fprintf('>'); +end + +fprintf('\n'); + +s = 1./sqrt(d); + +for j=1:nv, + v(:,j) = v(:,j)*s(j); +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize.m new file mode 100755 index 0000000..d166cd5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize.m @@ -0,0 +1,15 @@ +function [binv,bins] = binize(data,sig,bin_min,bin_max,num_bin) +% +% given an input data, and sigma which describes the uncertainty +% of the data, along with information on the bins, +% return the soft-hist on data +% + +ndata = length(data); + +bins = linspace(bin_min,bin_max,num_bin+1); +binv = zeros(num_bin,ndata); + +for j=1:num_bin, + binv(j,:) = erf((bins(j+1)-data)/sig) - erf((bins(j)-data)/sig); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize_old.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize_old.m new file mode 100755 index 0000000..d56d263 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binize_old.m @@ -0,0 +1,34 @@ +function [binv,bins] = binize(data,sig,bin_min,bin_max,num_bin) +% +% given an input data, and sigma which describes the uncertainty +% of the data, along with information on the bins, +% return the soft-hist on data +% + +ndata = length(data); + +if 0, +bins = linspace(bin_min,bin_max,num_bin); +binv = zeros(num_bin,ndata); + +Largev = 1000; + +bins = [-Largev,bins]; + +for j=1:num_bin, + binv(j,:) = erf((bins(j+1)-data)/sig) - erf((bins(j)-data)/sig); +end + +binv(num_bin,:) = binv(num_bin,:) + erf((Largev-data)/sig) - erf((bins(end)-data)/sig); +bins = bins(2:end); +else + +bins = linspace(bin_min,bin_max,num_bin+1); +binv = zeros(num_bin,ndata); + + +for j=1:num_bin, + binv(j,:) = erf((bins(j+1)-data)/sig) - erf((bins(j)-data)/sig); +end + +end \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binomialfield.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binomialfield.m new file mode 100755 index 0000000..d83d96d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/binomialfield.m @@ -0,0 +1,75 @@ +function [x,y,success] = BinomialField(n,sx,sy,ir,numtri); +%BF_HardCore Generates a hard core binomial field +% [x,y,success] = BinomialField(n,sx,sy,ir); +% n : # points (default 100) +% sx : size in x (default 100) +% sy : size in y (default 100) +% ir : inhibition radius (default 0) +% numtri : number of trials (default 200) +% x : x coordinates +% y : y coordinates +% success: whether success or not, useful when producing hard core model + +%% +%% (C) Thomas K. Leung +%% University of California at Berkeley +%% April 26, 1995. +%% leungt@cajal.cs.berkeley.edu +%% + +%% +%% Generate n points first and then reject those closer to the +%% previous points than ir +%% + +if nargin < 1 + n = 100; + sx = 100; + sy = 100; + ir = 0; + numtri = 200; +elseif (nargin == 1 | nargin == 2) + sx = 100; + sy = 100; + ir = 0; + numtri = 200; +elseif (nargin == 3) + ir = 0; + numtri = 200; +elseif (nargin == 4) + numtri = 200; +end + +x = zeros(1,n); +y = zeros(1,n); + +rand('seed',sum(100*clock)); +x(1) = rand(1) * sx; +y(1) = rand(1) * sy; + +success = 1; + +I = 2; +trial = 0; +while (I <= n & trial < numtri) + found = 0; + trial = 0; + while (~found & trial < numtri); + tx = rand(1) * sx; + ty = rand(1) * sy; + D = (x(1:(I-1)) - tx).^2 + (y(1:(I-1)) - ty).^2; + if sum(D > (ir^2)) == (I-1) + found = 1; + x(I) = tx; + y(I) = ty; + end + trial = trial + 1; + end + I = I + 1; +end + +if trial >= numtri + fprintf(1,'Failed to generate a point in %d trials\n',numtri); + success = 0; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize.m new file mode 100755 index 0000000..b03616f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize.m @@ -0,0 +1,9 @@ +function t1a = colize(t1,I1); + +t1a = t1; + +t1a = reshape(t1a,size(t1,1)*size(t1,2),1,size(t1,3)); +t1a = squeeze(t1a); +t1a = t1a'; + +%I1a = 2*I1(:)';I1a = I1a-mean(I1a(:));t1a = [I1a;t1a]; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_hist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_hist.m new file mode 100755 index 0000000..9c7b68e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_hist.m @@ -0,0 +1,29 @@ +function fh = colize_hist(fv,hb) +% (hb = sigs,bin_mins,bin_maxs,nbins) +% +% fv = [nfeature x npoints]; +% fh = [nfeatures*nbins x npoints]; +% +% take a feature matrix, and turn it into histogram bin feature matrix +% +% + +[nf,np] = size(fv); + +nbins = [0,hb.nbins]; +disp(sprintf('need matrix of %d x %d ',sum(nbins),np)); + +fh = zeros(sum(nbins),np); + +for k=1:nf, + bin_min = hb.bmins(k); + bin_max = hb.bmaxs(k); + nbin = nbins(k+1); + sig = hb.sigs(k); + fprintf('.'); + b = binize(fv(k,:),sig,bin_min,bin_max,nbin); + fh(sum(nbins(1:k))+1:sum(nbins(1:k+1)),:) = b; + +end + +fprintf('\n'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_s.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_s.m new file mode 100755 index 0000000..61c81ca --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_s.m @@ -0,0 +1,47 @@ +function fhs = colize_histnb_s(fh,Is,nw,hw) +% +% fhs = colize_histneigh(fh,fvs,nw) +% +% + +[tnbins,np] = size(fh); + +[nr,nc] = size(Is); + +st_sz = 2*hw + 1; + +nr_chank = floor(nr/st_sz); +nc_chank = floor(nc/st_sz); + +fhs = zeros(size(fh,1),nr_chank*nc_chank); + +idx = 0; +for k=1+hw:st_sz:nc-hw, + + fprintf('.'); + sk = max(1,k-nw); + ek = min(nc,k+nw); + + + % for each column, + for j=1+hw:st_sz:nr-hw, + sj = max(1,j-nw); + ej = min(nr,j+nw); + + id = j+(k-1)*nr; + idx = idx+1; + for li=sj:ej, + for lj=sk:ek, + idn = li+(lj-1)*nr; + + fhs(:,idx) = fhs(:,idx) + fh(:,idn); + + end + end + end +end + +fprintf('\n'); + + + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_sf.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_sf.m new file mode 100755 index 0000000..d0d60f9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histnb_sf.m @@ -0,0 +1,52 @@ +function fhs = colize_histnb_s(fvs,Is,hb,nw,hw) +% +% fhs = colize_histneigh(fvs,Is,hb,nw,hw) +% +% + +[nf,np] = size(fvs); + +[nr,nc] = size(Is); + +st_sz = 2*hw + 1; + +nr_chank = floor(nr/st_sz); +nc_chank = floor(nc/st_sz); + +tnbins = prod(hb.nbins(1:nf)); +disp(sprintf('allocat memory for %d x %d',tnbins,nr_chank*nc_chank)); + +fhs = zeros(tnbins,nr_chank*nc_chank); + +idx = 0; +for k=1+hw:st_sz:nc-hw, + + fprintf(','); + sk = max(1,k-nw); + ek = min(nc,k+nw); + + + % for each column, + for j=1+hw:st_sz:nr-hw, + sj = max(1,j-nw); + ej = min(nr,j+nw); + + id = j+(k-1)*nr; + idx = idx+1; + + %% find idx for the neighboring points + lis = [sj:ej]'*ones(1,ek-sk+1); + ljs = ones(ej-sj+1,1)*[sk:ek]; + idns = lis+(ljs-1)*nr; + + fh = colize_joint_hist(fvs(:,idns(:)),hb); + + fhs(:,idx) = sum(fh')'; + + end +end + +fprintf('\n'); + + + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histneighb.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histneighb.m new file mode 100755 index 0000000..6189cab --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_histneighb.m @@ -0,0 +1,37 @@ +function fhs = colize_histneigh(fh,Is,nw) +% +% fhs = colize_histneigh(fh,fvs,nw) +% +% + +[tnbins,np] = size(fh); + +[nr,nc] = size(Is); + +fhs = zeros(size(fh)); + +for j=1:nr, + fprintf('.'); + sj = max(1,j-nw); + ej = min(nr,j+nw); + + % for each column, + for k=1:nc, + sk = max(1,k-nw); + ek = min(nc,k+nw); + + id = j+(k-1)*nr; + + for li=sj:ej, + for lj=sk:ek, + idn = li+(lj-1)*nr; + + fhs(:,id) = fhs(:,id) + fh(:,idn); + end + end + end +end +fprintf('\n'); + + + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_joint_hist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_joint_hist.m new file mode 100755 index 0000000..e7844d8 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_joint_hist.m @@ -0,0 +1,41 @@ +function fh = colize_joint_hist(fv,hb) +% (hb = sigs,bin_mins,bin_maxs,nbins) +% +% take which histogram value and turn it into histogram bin +% + + + [nf,np] = size(fv); + + nbins = [0,hb.nbins]; + %disp(sprintf('need matrix of %d x %d ',prod(hb.nbins),np)); + + fh = zeros(hb.nbins(1),hb.nbins(2),np); + + k=1; + bin_min = hb.bmins(k); + bin_max = hb.bmaxs(k); + nbin = nbins(k+1); + sig = hb.sigs(k); + %fprintf('.'); + + b1 = binize(fv(k,:),sig,bin_min,bin_max,nbin); + k=2; + bin_min = hb.bmins(k); + bin_max = hb.bmaxs(k); + nbin = nbins(k+1); + sig = hb.sigs(k); + %fprintf('.'); + + b2 = binize(fv(k,:),sig,bin_min,bin_max,nbin); + + + for k=1:hb.nbins(1), + for j=1:hb.nbins(2), + fh(k,j,:) = b1(k,:).*b2(j,:); + end + end + +%fprintf('\n'); + +fh = reshape(fh,hb.nbins(1)*hb.nbins(2),np); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_test.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_test.m new file mode 100755 index 0000000..a9135cc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/colize_test.m @@ -0,0 +1,19 @@ +function t1a = colize(t1,I1); + +if 1, +t1a = t1; +%t1a = 1.2*half_sigmoid(t1,0.3,0.1);; +t1a = reshape(t1a,size(t1,1)*size(t1,2),1,size(t1,3)); +t1a = squeeze(t1a); +t1a = t1a'; + +%I1a = I1(:)';I1a = I1a-mean(I1a(:));t1a = [I1a;t1a]; + +else + mask = t1>=0; + t1a = abs(t1); + t1a = 0.5-t1a; + t1a = reshape(t1a,size(t1,1)*size(t1,2),1,size(t1,3)); + t1a = squeeze(t1a); + t1a = t1a'; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compact.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compact.m new file mode 100755 index 0000000..9863e0f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compact.m @@ -0,0 +1,36 @@ +function I = compact(img,ws) + + +%ws = 2*hws+1; + +[sy,sx] = size(img); + +rem_x = rem(sx,ws); +rem_y = rem(sy,ws); + +fix_x = ceil(sx/ws); +fix_y = ceil(sy/ws); + +fprintf('nr = %d, nc = %d\n',fix_y,fix_x); + +%startx= 1 + floor(rem_x*0.5)+hws; +%starty= 1 + floor(rem_y*0.5)+hws; + +I = zeros(fix_y,fix_x); + +yid = 0; +for j=1:ws:sy, + xid = 0; + yid = yid +1; + fprintf('.'); + for k=1:ws:sx, + xid = xid+1; + %I(yid,xid) = median(median(img(j-hws:j+hws,k-hws:k+hws))); + %I(yid,xid) = sum(sum(img(j-hws:j+hws,k-hws:k+hws))); + v = img(j:min(sy,j+ws-1),k:min(sx,k+ws-1)); + %I(yid,xid) = median(reshape(v,prod(size(v)),1)); + I(yid,xid) = median(median(v)); + end +end +fprintf('\n'); + \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_J.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_J.m new file mode 100755 index 0000000..99b8b69 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_J.m @@ -0,0 +1,31 @@ +function J = compute_J(A,I,size_x,size_y,D) +%% function J = compute_J(A,I,size_x,size_y,D) +% + +[center_x,center_y] = find_center(size_x,size_y); + +tmp = ones(size_y,1)*[1:size_x]; +index(:,1) = reshape(tmp,size_x*size_y,1)-center_x*ones(size_x*size_y,1); +index(:,2) = reshape(tmp',size_x*size_y,1)-center_y*ones(size_x*size_y,1); + +position_new = A*index'+ [D(1),0;0,D(2)]*ones(2,size_x*size_y); +position_new = round(position_new +... + [center_x,0;0,center_y]*ones(2,size_x*size_y)); +% we have to deal with out of boundary ones +% +bad_ones(1,:) = position_new(1,:)<1 | position_new(1,:)>size_x; +bad_ones(2,:) = position_new(2,:)<1 | position_new(2,:)>size_y; +bad = max([bad_ones(1,:);bad_ones(2,:)]); +good = ~bad; +% if new index is out of boundary, then set it to (0,0) +position_new(1,:) = position_new(1,:).*good; +position_new(2,:) = position_new(2,:).*good; + +new_index = size_y*(position_new(1,:)-ones(1,size_x*size_y))+... + position_new(2,:); +new_index = max([new_index;ones(1,size_x*size_y)]); +J = I(new_index); +% set the "out of boundary" to zero. +J = J.*good; +J = reshape(J',size_y,size_x); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_Lf.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_Lf.m new file mode 100755 index 0000000..7cda523 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_Lf.m @@ -0,0 +1,35 @@ +function dists = compute_Lf(F,cts,wz,nr,nc) + +gap = 2*wz(1)+1; +hw = wz(1); + +nr = nr+1; +nc = nc+1; + +dists = zeros(size(cts,1),nr*nc); +for ctj = 1:size(cts,1), + t1 = cutout(F,cts(ctj,:),wz); + + rid = 1; + fprintf('>'); + + for ri = hw+1:gap:size(F,1)-hw, + %fprintf('[%d]',ri); + cid = 1; + for ci = hw+1:gap:size(F,2)-hw, + %fprintf('(%d)',ci); + t2 = cutout(F,[ci,ri],wz); + + dist = abs(mean(t1(:))-mean(t2(:))); + + dists(ctj,rid+cid*nr) = max(dist,dists(ctj,rid+cid*nr)); + + cid = cid+1; + end + rid = rid+1; + end + + %fprintf('\n'); + +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_corr.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_corr.m new file mode 100755 index 0000000..92f9da4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_corr.m @@ -0,0 +1,10 @@ +function a = compute_corr(f,g) +% +% compute the circular correlation of f and g +% at points around zero +% +% + +ff = interp(f,4); +gg = interp(g,4); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff.m new file mode 100755 index 0000000..72bfe54 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff.m @@ -0,0 +1,36 @@ +function B = compute_diff(Ja,Jfa,hw,hnb); +% +% B = compute_diff(Ja,Jfa,hw,hnb) +% +% + +figure(1);%imagesc(Ja);axis('image'); +cs = round(ginput(1)); + +B = zeros(2*hnb+1,2*hnb+1); + +scales = [1:5];filter_ids = [1:7]; +Jc = get_win(Ja,cs,[hw,hw]); +Jfc= get_win5(Jfa,cs,[hw,hw]); +H2c = hist2d(Jc,Jfc,scales,filter_ids); + +figure(2);imagesc(Ja);axis('image');colormap(gray); +hold on; plot(cs(1),cs(2),'g*'); + + +for ii=-hnb:hnb, + for jj=-hnb:hnb, + J1 = get_win(Ja,cs+4*[jj,ii],[hw,hw]); + Jf1= get_win5(Jfa,cs+4*[jj,ii],[hw,hw]); + figure(2);plot(cs(1)+4*jj,cs(2)+4*ii,'ro');drawnow; + %figure(3);imagesc(J1);drawnow; + + H2 = hist2d(J1,Jf1,scales,filter_ids); + d = hist_diff(H2/prod(size(Jc)),H2c/prod(size(Jc))); + disp(sprintf('d=%f',d)); + B(ii+hnb+1,jj+hnb+1) = d; + + end +end + +figure(2);hold off; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch.m new file mode 100755 index 0000000..260b93a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch.m @@ -0,0 +1,34 @@ +function a = compute_diff_patch(gx1,gy1,gx2,gy2,I1,I2) +% +% a = compute_diff_patch(gx1,gy1,gx2,gy2,I1,I2) +% +% + +%ws = size(gx1); +%mask = smooth(ones(ws),2*max(ws)); +%mask = mask/sum(sum(mask)); + +%mag1= sum(sum(sqrt((mask.*gx1).^2 + (mask.*gy1).^2))); +%mag2= sum(sum(sqrt((mask.*gx2).^2 + (mask.*gy2).^2))); + +mag1= sum(sum(sqrt((gx1).^2 + (gy1).^2))); +mag2= sum(sum(sqrt((gx2).^2 + (gy2).^2))); + +P_tx1 = sigmoid(mag1,400,80); +P_tx2 = sigmoid(mag2,400,80); + +diff_I = mean(reshape(I1,prod(size(I1)),1))-... + mean(reshape(I2,prod(size(I2)),1)); +diff_I = abs(diff_I); + +s_g1 = [sum(sum(abs(gx1))),sum(sum(abs(gy1)))]; +s_g2 = [sum(sum(abs(gx2))),sum(sum(abs(gy2)))]; + +s_g1 = s_g1/(norm(s_g1)); +s_g2 = s_g2/(norm(s_g2)); + +a = (1-P_tx1)*(1-P_tx2)*exp(-diff_I/0.1) +... + P_tx1*P_tx2*(dot(s_g1,s_g2)); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch2.m new file mode 100755 index 0000000..9d2b528 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_diff_patch2.m @@ -0,0 +1,45 @@ +function [a,phi1,phi2] = compute_diff_patch(gx1,gy1,gx2,gy2,I1,I2) +% +% a = compute_diff_patch(gx1,gy1,gx2,gy2,I1,I2) +% +% + +%ws = size(gx1); +%mask = smooth(ones(ws),2*max(ws)); +%mask = mask/sum(sum(mask)); + +%mag1= sum(sum(sqrt((mask.*gx1).^2 + (mask.*gy1).^2))); +%mag2= sum(sum(sqrt((mask.*gx2).^2 + (mask.*gy2).^2))); + +mag1= sum(sum(sqrt((gx1).^2 + (gy1).^2)))/prod(size(gx1)); +mag2= sum(sum(sqrt((gx2).^2 + (gy2).^2)))/prod(size(gx1)); + +P_tx1 = sigmoid(mag1,2,0.5); +P_tx2 = sigmoid(mag2,2,0.5); + +diff_I = mean(reshape(I1,prod(size(I1)),1))-... + mean(reshape(I2,prod(size(I2)),1)); +diff_I = abs(diff_I); + +[l1,l2,phi1] = mwis(gx1,gy1); +[k1,k2,phi2] = mwis(gx2,gy2); + +ratio1 = min([l1,l2])/max([l1,l2]); +ratio2 = min([k1,k2])/max([k1,k2]); + +r1 = 1-sigmoid(ratio1,0.35,0.05); +r2 = 1-sigmoid(ratio2,0.35,0.05); + +s1 = [cos(phi1),sin(phi1)]; +s2 = [cos(phi2),sin(phi2)]; + +angle = acos(abs(dot(s1,s2)))*180/pi; + +a1 = (1-P_tx1*P_tx2)*exp(-diff_I/0.1); +a2 = P_tx1*P_tx2*(r1*r2*(90-angle)/90); +a3 = P_tx1*P_tx2*((1-r1*r2)*(1-sigmoid(abs(r1-r2),0.3,0.04))); + +a = a1+a2+a3; + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter.m new file mode 100755 index 0000000..04e78e1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter.m @@ -0,0 +1,84 @@ +function [filter_output,filters] = compute_filter(I,sig,r,sz); +% +% +% + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +as = ori_offset:ori_incr:180+ori_offset-ori_incr; + +filter_output = []; +filters = []; + +wsz = 2*round(sz) + 1; +M1 = wsz(1);M2 = wsz(2); + +%%%%% prepare FFT of image %%%%%%%%%%%%% + +[N1,N2]=size(I); +tmp=zeros(size(I)+[M1-1 M2-1]); +tmp(1:N1,1:N2)=I; +IF=fft2(tmp); + + +%%%%%%%%%% filtering stage %%%%%%%%%%% +if size(sig,2)== 1, + + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + + g = g - mean(reshape(g,prod(size(g)),1)); + + g = g/sum(sum(abs(g))); + + filters(:,:,j) = g; + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = If.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + + filter_output(:,:,j) = Ig; + end +else + + % there are multiple scales + sigs = sig; + szs = sz; + for k = 1:size(sigs,2), + sig = sigs(k); + sz = szs(k); + fprintf('%d',k); + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + g = g - mean(reshape(g,prod(size(g)),1)); + g = g/sum(sum(abs(g))); + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = If.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + %c = conv2(I,g,'same'); + + filter_output(:,:,j,k) = Ig; + filters(:,:,j,k) = g; + end + + + end + +end + +fprintf('\n'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter_fft.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter_fft.m new file mode 100755 index 0000000..359c6ba --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/compute_filter_fft.m @@ -0,0 +1,84 @@ +function [filter_output,filters] = compute_filter_fft(I,sig,r,sz,num_ori); +% +% +% + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +as = ori_offset:ori_incr:180+ori_offset-ori_incr; + +filter_output = []; +filters = []; + +wsz = 2*round(sz(end)) + 1; +M1 = wsz;M2 = wsz; + +%%%%% prepare FFT of image %%%%%%%%%%%%% + +[N1,N2]=size(I); +tmp=zeros(size(I)+[M1-1 M2-1]); +tmp(1:N1,1:N2)=I; +IF=fft2(tmp); + + +%%%%%%%%%% filtering stage %%%%%%%%%%% +if size(sig,2)== 1, + + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + + g = g - mean(reshape(g,prod(size(g)),1)); + + g = g/sum(sum(abs(g))); + + filters(:,:,j) = g; + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = IF.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + + filter_output(:,:,j) = Ig; + end +else + + % there are multiple scales + sigs = sig; + szs = sz; + for k = 1:size(sigs,2), + sig = sigs(k); + sz = szs(end); + fprintf('%d',k); + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + g = g - mean(reshape(g,prod(size(g)),1)); + g = g/sum(sum(abs(g))); + + gF = fft2(g,N1+M1-1,N2+M2-1); + IgF = IF.*gF; + Ig = real(ifft2(IgF)); + Ig = Ig(ceil((M1+1)/2):ceil((M1+1)/2)+N1-1,ceil((M2+1)/2):ceil((M2+1)/2)+N2-1); + + %c = conv2(I,g,'valid'); + %c = conv2(I,g,'same'); + + filter_output(:,:,j,k) = Ig; + filters(:,:,j,k) = g; + end + + + end + +end + +fprintf('\n'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/conv_trim.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/conv_trim.m new file mode 100755 index 0000000..30d16a9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/conv_trim.m @@ -0,0 +1,6 @@ +% trims an array to remove meaningless pixels after a convolution with +% an r * c window + +function[B] = conv_trim(A, r, c) + +B = A(r+1:size(A,1)-r, c+1:size(A,2)-c); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/corr_hist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/corr_hist.m new file mode 100755 index 0000000..c538dc1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/corr_hist.m @@ -0,0 +1,9 @@ +function alpha = corr_hist(hists) + +[y,x,v] = find(hists); +mx = sum(x.*v)/sum(v); +my = sum(y.*v)/sum(v); + +top = sum( (x-mx).*(y-my).*v); +bottom = sqrt(sum( ((x-mx).^2).*v))*sqrt(sum( ((y-my).^2).*v)); +alpha = top/bottom; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/crop_im_fil.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/crop_im_fil.m new file mode 100755 index 0000000..5472171 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/crop_im_fil.m @@ -0,0 +1,11 @@ +function [J,f,rect] = crop_im_fil(Ja,Jfa,fig_id) +% +% + +figure(fig_id); +imagesc(Ja);axis('image'); + +[J,rect] = imcrop;rect = round(rect); +J = Ja(rect(2):rect(2)+rect(4),rect(1):rect(1)+rect(3)); +f = Jfa(rect(2):rect(2)+rect(4),rect(1):rect(1)+rect(3),:,:); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutoff.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutoff.m new file mode 100755 index 0000000..58c6b94 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutoff.m @@ -0,0 +1,13 @@ +function I = cutoff(I,wc) +% +% + +nr = size(I,1); +nc = size(I,2); + +if ndims(I) == 3, +I = I(wc+1:nr-wc,wc+1:nc-wc,:,:); +else +I = I(wc+1:nr-wc,wc+1:nc-wc,:,:); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutout.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutout.m new file mode 100755 index 0000000..b80f27b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/cutout.m @@ -0,0 +1,3 @@ +function a = cutout(I,ct,wz); + +a = I(ct(2)-wz(2):ct(2)+wz(2),ct(1)-wz(1):ct(1)+wz(1),:); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_Imask.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_Imask.m new file mode 100755 index 0000000..dbd7fdb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_Imask.m @@ -0,0 +1,20 @@ +function Imasks = disp_Imask(Is,nr,nc,hw,masks) +% +% Imasks = disp_Imask(Is,nr,nc,hw,masks) +% + +%hw = 2; %nr = 43;nc=68; +gap = 2*hw+1; + +x = [1:nc*gap]; +y = [1:nr*gap]; + +xs = (x-hw-1)/gap + 1;ys = (y-hw-1)/gap + 1; + +for gid=1:size(masks,3), + tmp = interp2(reshape(masks(:,:,gid),nr,nc),xs,ys'); + + Imasks(:,:,gid) = (tmp>0.52).* ((Is).^0.8); + subplot(3,3,gid); + im(Imasks(:,:,gid)); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_diff.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_diff.m new file mode 100755 index 0000000..090c273 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_diff.m @@ -0,0 +1,37 @@ +function disp_diff(H1,H2) +% +% disp_diff(H1,H2) +% + +ns = size(H1,3); +nf = size(H1,4); + +H1 = H1/49; +H2 = H2/49; + + +sI= [1,0,1];sI = exp(-sI); +sI = sI/sum(sI); + +for j = 1:ns, + for k = 1:nf, + h1 = H1(:,:,j,k); + h2 = H2(:,:,j,k); + + subplot(ns,nf,(j-1)*nf+k); + h1s = conv2(conv2(h1,sI','same'),sI,'same'); + h2s = conv2(conv2(h2,sI','same'),sI,'same'); + + [is,js] = find( (h1>0) | (h2>0)); + ids = (js-1)*size(h1,1) + is; + + hdiff = abs(h1s-h2s).*((h1>0) | (h2>0)); + + xdiff = ((h1(ids)-h2(ids)).*(h1(ids)-h2(ids)))./(h1(ids)+h2(ids)); + + xdiffs = ((h1s(ids)-h2s(ids)).*(h1s(ids)-h2s(ids)))./(h1s(ids)+h2s(ids)); + imagesc(hdiff);colorbar;axis('off'); +% title(sprintf('%3.3f, %3.3f',sum(sum(hdiff))/49,sum(sum(abs(h1-h2)))/49));drawnow; + title(sprintf('%3.3f, %3.3f',sum(xdiff),sum(xdiffs)));drawnow + end +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult.m new file mode 100755 index 0000000..e07556a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult.m @@ -0,0 +1,435 @@ +%fn = '134035'; +%fn = '130040'; +%fn = '334074'; +fn = '130065'; + +%basedir = 'plaatje_data/olddata/'; +% basedir = 'data/'; nr = 49;nc =30; + +basedir = 'plaatje_data/'; + +fname = sprintf('%s%s_eigvec.pfm',basedir,fn); +eigv = readpfm(fname); +fname = sprintf('%s%s_eigval.pfm',basedir,fn); +eigval = readpfm(fname); + +fname = sprintf('%s%s_ncutvec.pfm',basedir,fn); +ncutv = readpfm(fname); +fname = sprintf('%s%s_ncutval.pfm',basedir,fn); +ncutval = readpfm(fname); + +%fname = sprintf('images/130039.pgm'); +fname = sprintf('images/%s.pgm',fn); +I = readpgm(fname); +cutsz = 20; I = cutoff(I,cutsz); +figure(3);im(I);colormap(gray); + +new = 0; + +if ~new, + + %nr = 49;nc = 30; + nr = 30;nc = 49; + +%nr = 68;nc = 43; +%nc = 68;nr = 43; + +else + + fn1 = fn; + fn = 'test'; + fname = sprintf('plaatje_data/%s_gcs.pfm',fn); + gcs = readpfm(fname); + + fname = sprintf('plaatje_data/%s_gce.pfm',fn); + gce = readpfm(fname); + + fname = sprintf('plaatje_data/%s_grs.pfm',fn); + grs = readpfm(fname); + + fname = sprintf('plaatje_data/%s_gre.pfm',fn); + gre = readpfm(fname); + + nr = max(gre(:))+1; + nc = max(gce(:))+1; + + fn = fn1; + +end + +figure(6); +for j=1:8, + subplot(3,3,j); + im(reshape(ncutv(:,j+1),nr,nc));colorbar + title(num2str(ncutval(j+1,1))); +end +%cm = sprintf('print -dps ncut_%s',fn);disp(cm);eval(cm); +subplot(3,3,9);im(I);axis('off'); + +figure(7);clf +for j=1:12, + subplot(3,4,j); + im(reshape(eigv(:,j),nr,nc));colorbar;%axis('off'); + title(sprintf('%3.4e',eigval(j,1))); +end +%cm = sprintf('print -dps eig_%s',fn);disp(cm);eval(cm); + +%%%%%%%%%%% + +ev = eigval(:,1); +figure(5);hold off;clf;subplot(1,2,1); +%semilogy((ev(1:end-1) - ev(2:end))./ev(1:end-1),'x-');grid on; +plot((ev(1:end-1) - ev(2:end))./ev(1:end-1),'x-');grid on; +%semilogy(0.01*ones(size(ev(2:end-1))),'r-');semilogy(0.005*ones(size(ev(2:end-1))),'r-');semilogy(0.0025*ones(size(ev(2:end-1))),'r-');grid on;hold off; +subplot(1,2,2); +%semilogy(ev(1:end-1)-ev(2:end),'p-');grid on; +semilogy((ev(1:end-1) - ev(2:end))/ev(1),'x-');grid on; + + +if 0, + +fname = sprintf('plaatje_data/ncutval_%s.pfm',fn); +nval = readpfm(fname); +fname = sprintf('plaatje_data/ncutvec_%s.pfm',fn); +nv = readpfm(fname); + +figure(2); +nvv = size(nv,2); +for j=1:min(5,nvv-1), + subplot(1,min(5,nvv-1),j); + ims(nv(:,j+1),nr,nc); +end + + +%figure(5); +%subplot(2,2,1);plot(eigval(:,1),'x-'); + + +if 0, + +fname = 130039; +for j=0:20, + cm = sprintf('!cp plaatje_data/%d_%d.pfm plaatje_data/test_%d.pfm ',fname,j,j); + disp(cm);eval(cm); +end + +%%%%%%%% +fnout = 'test';fn_t = '334003'; +cm = sprintf('!cp plaatje_data/%s_eigval.pfm %s_eigval.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp plaatje_data/%s_eigvec.pfm %s_eigvec.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp plaatje_data/%s_ncutvec.pfm %s_ncutvec.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp plaatje_data/%s_ncutval.pfm %s_ncutval.pfm',fnout,fn_t); +disp(cm);eval(cm); + + + + +end + +disp_flag = 0; +if disp_flag, + [I1,bnr,bnc] = proj_back_id(ncutv(:,2),gcs,gce,grs,gre); + imvs(I,I1>0.002,bnr,bnc); +end + +if 0, + + nv = 3; + A = eigv(:,1:nv)*eigv(:,1:nv)'; + [v,d] = ncut(abs(A),min(nv,5)); + + figure(3); + for j=1:min(nv,5), + subplot(2,2,j); + ims(v(:,j),nr,nc); + end + +end + +%%%%%%%% + +figure(4);%im(I);colorbar; +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1)); +ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; + +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(5);im(abs(reshape(A(idx,:),nr,nc)));%colorbar; + + + + %%%%% + + fname = 'test2'; + fn = sprintf('plaatje_data/ncut_%s.pfm',fname); + ncutv1 = readpfm(fn); + nr = 30; nc=49; + + figure(1); + for j=1:min(4,size(ncutv1,2)), + subplot(2,2,j); + ims(ncutv1(:,j+1),nr,nc); + end + + + +%%%%%%%%%% + + id = 0; + fn = sprintf('plaatje_data/test_Aa%d.pfm',id); + disp(sprintf('A = readpfm(%s);',fn)); + A = readpfm(fn); + + cm = sprintf('[v%d,d%d] = eigs(A,12);',id,id); + disp(cm);eval(cm); + + writepfm('test_eigv0.pfm',v0); + writepfm('test_eigva0.pfm',diag(d0)); + + + + +vs = zeros(size(v1,1),size(v1,2),6); +ds = zeros(length(d1),6); + +for j=0:5, + cm = sprintf('vs(:,:,%d) = v%d;',j+1,j); + disp(cm);eval(cm); + cm = sprintf('d = diag(d%d);',j); + disp(cm);eval(cm); + cm = sprintf('ds(:,%d) = d(:);',j+1); + disp(cm);eval(cm); + + +end + +%save evsum vs ds + +figure(1);nr = 49;nc=30;evid = 3; +for j=1:12,subplot(3,4,j);ims(vs(:,j,evid),nr,nc);end + +I = readpgm('images/334039.pgm');I = cutoff(I,20); + +As = zeros(6,nr*nc); + +figure(3);%im(I);colormap(gray); +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1;ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(5); + +figure(4);nvs = [6,9,12,12,12,12]; +for evid = 1:5,As(evid,:) = squeeze(vs(idx,1:nvs(evid),evid))*squeeze(vs(:,1:nvs(evid),evid))';end +for evid =1:5,subplot(2,3,evid);im(abs(reshape(As(evid,:),nr,nc)));colorbar;end +subplot(2,3,6);ims(sum(abs(As)),nr,nc);colorbar + +%%%%%%%%% + +%%%%%% eig of the As over all scales %% + +A = zeros(nr*nc,nr*nc); + +for evid=1:5, disp(evid); + A = A + abs(squeeze(vs(:,1:nvs(evid),evid))*squeeze(vs(:,1:nvs(evid),evid))'); +end + +[v,d] = eigs(A,12); +figure(1); for j=1:12, subplot(3,4,j);ims(v(:,j),nr,nc);end + +[vn,dn] = ncut_b(A,12); +figure(3); for j=1:12, subplot(3,4,j);ims(-vn(:,j),nr,nc);end + +nv = 6; +A = abs(eigv(:,1:nv)*eigv(:,1:nv)'); +[v,d] = ncut_b(A,nv+1); +figure(1); +nv = 4; +for j=1:nv,subplot(2,nv,j);ims(v(:,j+1),nr,nc);title(sprintf('%3.3e',d(j+1)));end + +for j=1:nv,subplot(2,nv,j+nv);ims(eigv(:,j),nr,nc);title(sprintf('%3.3e',eigval(j,1)));end + +%%%%%%%%%%%%% + +while 1, +figure(3);%im(I);colormap(gray); +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1;ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(1); +ims(exp(-(A(idx,:))/(0.02^2)),nr,nc);colorbar +end + + + +%%%%%%%%%%%%%% + +figure(3); +hw = 3;st_sz = 2*hw+1; +np = 20; +ct = round(ginput(np)); +ct_chank =[]; +ct_chank(:,1) = round((ct(:,1)-hw-1)/st_sz) + 1; +ct_chank(:,2) = round((ct(:,2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +%As = readpfm_id('plaatje_data/130040_AX.pfm',idx,2924); +As = readpfm_idf('plaatje_data/tmp/134035_AX3.pfm',idx,nr*nc); + +%save dist_data2 As idx ct_chank ct hw nr nc eigv eigval + +%load dist_data1a + +set(gcf,'DefaultLineLinewidth',5); + +minA = min(min(As)); +figure(1);clf; hold off; +set(gcf,'DefaultLineLinewidth',2); +for id = 1:np, +subplot(4,5,id); +%image(2.8e-2*((-minA)+reshape(As(id,:),nr,nc)));axis('image');axis('off');hold on +ims(-As(id,:),nr,nc);axis('off');hold on +plot(ct_chank(id,1)+1,ct_chank(id,2)+1,'rx');hold off; +end + +figure(1);clf;hold off; +nvv = 6 +set(gcf,'DefaultLineLinewidth',1); +for id=1:np, + At = abs(eigv(idx(id),1:nvv)*eigv(:,1:nvv)'); + subplot(4,5,id); + %image(2.5e4*reshape(At,nr,nc));axis('image');axis('off');hold on + ims(At,nr,nc);axis('off');hold on; + plot(ct_chank(id,1)+1,ct_chank(id,2)+1,'rx');hold off; +end + + +print_flag =0; +if print_flag, + fn = '130040'; + + figure(4);clf; + colormap(gray); + set(gcf,'DefaultLineLinewidth',7); + + for id =1:np, + %image(2.8e-2*((-minA)+reshape(As(id,:),nr,nc)));axis('image');axis('off');hold on + ims(-As(id,:),nr,nc);axis('off'); + hold on;plot(ct_chank(id,1)+1,ct_chank(id,2)+1,'rp');hold off; + cm = sprintf('print -deps dist_x1_%s_%d',fn,id); + disp(cm);eval(cm); + end + + nvv = 5; + set(gcf,'DefaultLineLinewidth',7); + figure(4);colormap(gray); + for id=1:np, + At = abs(eigv(idx(id),1:nvv)*eigv(:,1:nvv)'); + %image(1.5e4*reshape(At,nr,nc));axis('image');axis('off');%hold on + ims(At,nr,nc);axis('off');%hold on; + %plot(ct_chank(id,1)+1,ct_chank(id,2)+1,'rp');hold off; + cm = sprintf('print -deps dist_d_%s_%d',fn,id); + disp(cm);eval(cm); + end + + % print eigvects + for j=1:size(eigv,2), + ims(eigv(:,j),nr,nc);axis('off'); + cm = sprintf('print -deps eigv_%s_%d',fn,j); + disp(cm);eval(cm); + end + + for j=1:size(ncutv,2), + ims(ncutv(:,j),nr,nc);axis('off'); + cm = sprintf('print -deps ncutv_%s_%d',fn,j); + disp(cm);eval(cm); + end + + +end + +basedir ='plaatje_data/newdata/'; +fname = sprintf('%s%s_eigvec.pfm',basedir,fn); +eigv = readpfm(fname); + +ix = 1; +figure(5);colormap(gray);clf +for j=1:7, + for k=[2,3,4,6,9,12]; + subplot(7,6,ix); + At = abs(eigv(idx(j),1:k)*eigv(:,1:k)'); + ims(At,nr,nc);axis('off');%colorbar; + if (k==2), + hold on; plot(ct_chank(j,1),ct_chank(j,2),'rp');hold off; + title(num2str(j)); + end + ix = ix+1; + end +end + +figure(4);clf;colormap(gray); +set(gcf,'DefaultLineLinewidth',7); +for j=1:20, + for k=[2,3,4,6,9,12]; + + At = abs(eigv(idx(j),1:k)*eigv(:,1:k)'); + ims(At,nr,nc);axis('off');%colorbar; + if (k==2), + hold on; plot(ct_chank(j,1),ct_chank(j,2),'rp');hold off; + end + + cm = sprintf('print -deps dist_scale_65_%d_%d',j,k); + disp(cm);eval(cm); + + end +end + +base_dir = 'plaatje_data/'; + +% cts are the centers, + +wz = [hw,hw]; +gap = 2*hw+1; +dist = zeros(size(cts,1),(nr+1)*(nc+1)); + +for j=1:1:24, + fn = sprintf('%s134035_%d.pfm',base_dir,j); + disp(fn); + F = readpfm(fn); + + dists = compute_Lf(F,cts,wz,nr,nc); + dist = max(dists,dist); + +end + + +figure(4);clf;colormap(gray); +set(gcf,'DefaultLineLinewidth',7); + +ids = [8,1,12,5,10,15]; + +for j=1:6, + + d = reshape(dist(j,:),nr+1,nc+1); + d = d(1:end-1,2:end); + im(-d);axis('off'); hold on; + plot(ct_chank(ids(j),1),ct_chank(ids(j),2),'p'); + hold off + + cm = sprintf('print -deps dist_lf_65_%d',j); + disp(cm);eval(cm); + + pause; +end + + + +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult2.m new file mode 100755 index 0000000..46239ef --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresult2.m @@ -0,0 +1,215 @@ +fn = '130042'; + +fname = sprintf('data/%s_eigvec.pfm',fn); +eigv = readpfm(fname); +fname = sprintf('data/%s_eigval.pfm',fn); +eigval = readpfm(fname); + +fname = sprintf('data/%s_ncutvec.pfm',fn); +ncutv = readpfm(fname); +fname = sprintf('data/%s_ncutval.pfm',fn); +ncutval = readpfm(fname); + +%fname = sprintf('images/130038.pgm'); +fname = sprintf('images/%s.pgm',fn); +I = readpgm(fname); +cutsz = 20; I = cutoff(I,cutsz); +figure(3);im(I);colormap(gray); + +new = 0; + +if new, + fn1 = fn; + fn = 'test'; + fname = sprintf('data/%s_gcs.pfm',fn); + gcs = readpfm(fname); + + fname = sprintf('data/%s_gce.pfm',fn); + gce = readpfm(fname); + + fname = sprintf('data/%s_grs.pfm',fn); + grs = readpfm(fname); + + fname = sprintf('data/%s_gre.pfm',fn); + gre = readpfm(fname); + + nr = max(gre(:))+1; + nc = max(gce(:))+1; + + fn = fn1; + +else + %nr = 49;nc = 30; + nr = 30;nc = 49; + +end + +figure(6); +for j=1:8, + subplot(3,3,j); + im(reshape(ncutv(:,j+1),nr,nc));colorbar + title(num2str(ncutval(j+1,1))); +end +%cm = sprintf('print -dps ncut_%s',fn);disp(cm);eval(cm); +subplot(3,3,9);im(I);axis('off'); + +figure(7);clf +for j=1:9, + subplot(3,3,j); + im(reshape(eigv(:,j),nr,nc));colorbar;%axis('off'); + title(sprintf('%3.4e',eigval(j,1))); +end +%cm = sprintf('print -dps eig_%s',fn);disp(cm);eval(cm); + + + +if 0, + +fname = 130042; +for j=0:30, + cm = sprintf('!cp plaatje_data/%d_%d.pfm data/%d_%d.pfm ',fname,j,fname,j); + disp(cm);eval(cm); +end + +%%%%%%%% +fnout = '130042';fn_t = '130042'; +cm = sprintf('!cp data/%s_eigval.pfm %s_eigval.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp data/%s_eigvec.pfm %s_eigvec.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp data/%s_ncutvec.pfm %s_ncutvec.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp data/%s_ncutval.pfm %s_ncutval.pfm',fnout,fn_t); +disp(cm);eval(cm); + + + + +end + +disp_flag = 0; +if disp_flag, + [I1,bnr,bnc] = proj_back_id(ncutv(:,2),gcs,gce,grs,gre); + imvs(I,I1>0.002,bnr,bnc); +end + +if 0, + + nv = 3; + A = eigv(:,1:nv)*eigv(:,1:nv)'; + [v,d] = ncut(abs(A),min(nv,5)); + + figure(3); + for j=1:min(nv,5), + subplot(2,2,j); + ims(v(:,j),nr,nc); + end + + +%%%%%%%% + +figure(4);%im(I);colorbar; +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1)); +ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; + +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(5);im(abs(reshape(A(idx,:),nr,nc)));%colorbar; + + + + %%%%% + + fname = 'test2'; + fn = sprintf('data/ncut_%s.pfm',fname); + ncutv1 = readpfm(fn); + nr = 30; nc=49; + + figure(1); + for j=1:min(4,size(ncutv1,2)), + subplot(2,2,j); + ims(ncutv1(:,j+1),nr,nc); + end + + + +%%%%%%%%%% + + id = 0; + fn = sprintf('data/test_Aa%d.pfm',id); + disp(sprintf('A = readpfm(%s);',fn)); + A = readpfm(fn); + + cm = sprintf('[v%d,d%d] = eigs(A,12);',id,id); + disp(cm);eval(cm); + + writepfm('test_eigv0.pfm',v0); + writepfm('test_eigva0.pfm',diag(d0)); + + + + +vs = zeros(size(v1,1),size(v1,2),6); +ds = zeros(length(d1),6); + +for j=0:5, + cm = sprintf('vs(:,:,%d) = v%d;',j+1,j); + disp(cm);eval(cm); + cm = sprintf('d = diag(d%d);',j); + disp(cm);eval(cm); + cm = sprintf('ds(:,%d) = d(:);',j+1); + disp(cm);eval(cm); + + +end + +%save evsum vs ds + +figure(1);nr = 49;nc=30;evid = 3; +for j=1:12,subplot(3,4,j);ims(vs(:,j,evid),nr,nc);end + +I = readpgm('images/334039.pgm');I = cutoff(I,20); + +As = zeros(6,nr*nc); + +figure(3);%im(I);colormap(gray); +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1;ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(5); + +figure(4);nvs = [6,9,12,12,12,12]; +for evid = 1:5,As(evid,:) = squeeze(vs(idx,1:nvs(evid),evid))*squeeze(vs(:,1:nvs(evid),evid))';end +for evid =1:5,subplot(2,3,evid);im(abs(reshape(As(evid,:),nr,nc)));colorbar;end +subplot(2,3,6);ims(sum(abs(As)),nr,nc);colorbar + +%%%%%%%%% + +%%%%%% eig of the As over all scales %% + +A = zeros(nr*nc,nr*nc); + +for evid=1:5, disp(evid); + A = A + abs(squeeze(vs(:,1:nvs(evid),evid))*squeeze(vs(:,1:nvs(evid),evid))'); +end + +[v,d] = eigs(A,12); +figure(1); for j=1:12, subplot(3,4,j);ims(v(:,j),nr,nc);end + +[vn,dn] = ncut_b(A,12); +figure(3); for j=1:12, subplot(3,4,j);ims(-vn(:,j),nr,nc);end + +nv = 6; +A = abs(eigv(:,1:nv)*eigv(:,1:nv)'); +[v,d] = ncut_b(A,nv+1); +figure(1); +for j=1:nv,subplot(2,nv,j);ims(v(:,j+1),nr,nc);title(sprintf('%3.3e',d(j+1)));end + +for j=1:nv,subplot(2,nv,j+nv);ims(eigv(:,j),nr,nc);title(sprintf('%3.3e',eigval(j,1)));end + + + +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresulthome.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresulthome.m new file mode 100755 index 0000000..208b86a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_evresulthome.m @@ -0,0 +1,237 @@ +fn = '334003'; + +fname = sprintf('%s_eigvec.pfm',fn); +disp(sprintf('reading %s',fname)); +eigv = readpfm(fname); +fname = sprintf('%s_eigval.pfm',fn); +eigval = readpfm(fname); + +fname = sprintf('%s_ncutvec.pfm',fn); +ncutv = readpfm(fname); +fname = sprintf('%s_ncutval.pfm',fn); +ncutval = readpfm(fname); + +%fname = sprintf('images/130038.pgm'); +fname = sprintf('images/%s.pgm',fn); +I = readpgm(fname); +cutsz = 20; I = cutoff(I,cutsz); +figure(3);im(I);colormap(gray); + +new = 0; + +if new, + fn1 = fn; + fn = 'test'; + fname = sprintf('%s_gcs.pfm',fn); + gcs = readpfm(fname); + + fname = sprintf('%s_gce.pfm',fn); + gce = readpfm(fname); + + fname = sprintf('%s_grs.pfm',fn); + grs = readpfm(fname); + + fname = sprintf('%s_gre.pfm',fn); + gre = readpfm(fname); + + nr = max(gre(:))+1; + nc = max(gce(:))+1; + + fn = fn1; + +else + nr = 49;nc = 30; + %nr = 30;nc = 49; + +end + +figure(6); +for j=1:8, + subplot(3,3,j); + im(reshape(ncutv(:,j+1),nr,nc));colorbar + title(num2str(ncutval(j+1,1))); +end +%cm = sprintf('print -dps ncut_%s',fn);disp(cm);eval(cm); +%subplot(3,3,9);im(I);axis('off'); + +figure(7);clf +for j=1:9, + subplot(3,3,j); + im(reshape(eigv(:,j),nr,nc));colorbar;%axis('off'); + title(sprintf('%3.4e',eigval(j,1))); +end +%cm = sprintf('print -dps eig_%s',fn);disp(cm);eval(cm); + + + +if 0, + +fname = 130042; +for j=0:30, + cm = sprintf('!cp plaatje_data/%d_%d.pfm data/%d_%d.pfm ',fname,j,fname,j); + disp(cm);eval(cm); +end + +%%%%%%%% +fnout = '130042';fn_t = '130042'; +cm = sprintf('!cp data/%s_eigval.pfm %s_eigval.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp data/%s_eigvec.pfm %s_eigvec.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp data/%s_ncutvec.pfm %s_ncutvec.pfm',fnout,fn_t); +disp(cm);eval(cm); +cm = sprintf('!cp data/%s_ncutval.pfm %s_ncutval.pfm',fnout,fn_t); +disp(cm);eval(cm); + + + + +end + +disp_flag = 0; +if disp_flag, + [I1,bnr,bnc] = proj_back_id(ncutv(:,2),gcs,gce,grs,gre); + imvs(I,I1>0.002,bnr,bnc); +end + +if 0, + + nv = 3; + A = eigv(:,1:nv)*eigv(:,1:nv)'; + [v,d] = ncut(abs(A),min(nv,5)); + + figure(3); + for j=1:min(nv,5), + subplot(2,2,j); + ims(v(:,j),nr,nc); + end + + +%%%%%%%% + +figure(4);%im(I);colorbar; +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1)); +ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; + +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(5);im(abs(reshape(A(idx,:),nr,nc)));%colorbar; + + + + %%%%% + + fname = 'test2'; + fn = sprintf('data/ncut_%s.pfm',fname); + ncutv1 = readpfm(fn); + nr = 30; nc=49; + + figure(1); + for j=1:min(4,size(ncutv1,2)), + subplot(2,2,j); + ims(ncutv1(:,j+1),nr,nc); + end + + + +%%%%%%%%%% + + id = 0; + fn = sprintf('test_Aa%d.pfm',id); + disp(sprintf('A = readpfm(%s);',fn)); + A = readpfm(fn); + + cm = sprintf('[v%d,d%d] = eigs(A,12);',id,id); + disp(cm);eval(cm); + + writepfm('test_eigv0.pfm',v0); + writepfm('test_eigva0.pfm',diag(d0)); + + + + +vs = zeros(size(v1,1),size(v1,2),6); +ds = zeros(length(d1),6); + +for j=0:5, + cm = sprintf('vs(:,:,%d) = v%d;',j+1,j); + disp(cm);eval(cm); + cm = sprintf('d = diag(d%d);',j); + disp(cm);eval(cm); + cm = sprintf('ds(:,%d) = d(:);',j+1); + disp(cm);eval(cm); + + +end + +%save evsum vs ds + +figure(1);nr = 49;nc=30;evid = 3; +for j=1:12,subplot(3,4,j);ims(vs(:,j,evid),nr,nc);end + +I = readpgm('images/334039.pgm');I = cutoff(I,20); + +As = zeros(6,nr*nc); + +figure(3);%im(I);colormap(gray); +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1;ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(5); + +figure(4);nvs = [6,9,12,12,12,12]; +for evid = 1:5,As(evid,:) = squeeze(vs(idx,1:nvs(evid),evid))*squeeze(vs(:,1:nvs(evid),evid))';end +for evid =1:5,subplot(2,3,evid);im(abs(reshape(As(evid,:),nr,nc)));colorbar;end +subplot(2,3,6);ims(sum(abs(As)),nr,nc);colorbar + +%%%%%%%%% + +%%%%%% eig of the As over all scales %% + +A = zeros(nr*nc,nr*nc); + +for evid=1:5, disp(evid); + A = A + abs(squeeze(vs(:,1:nvs(evid),evid))*squeeze(vs(:,1:nvs(evid),evid))'); +end + +[v,d] = eigs(A,12); +figure(1); for j=1:12, subplot(3,4,j);ims(v(:,j),nr,nc);end + +[vn,dn] = ncut_b(A,12); +figure(3); for j=1:12, subplot(3,4,j);ims(-vn(:,j),nr,nc);end + +nv = 6; +A = abs(eigv(:,1:nv)*eigv(:,1:nv)'); +[v,d] = ncut_b(A,nv+1); +figure(1); +for j=1:nv,subplot(2,nv,j);ims(v(:,j+1),nr,nc);title(sprintf('%3.3e',d(j+1)));end + +for j=1:nv,subplot(2,nv,j+nv);ims(eigv(:,j),nr,nc);title(sprintf('%3.3e',eigval(j,1)));end + +%%%%%%%%%%%%%%%%% + +while 1, +figure(3);%im(I);colormap(gray); +hw = 3;st_sz = 2*hw+1; +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1;ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr + ct_chank(:,2); + +figure(1); +ims(exp(-(A(idx,:))/(0.03^2)),nr,nc);colorbar +end + +disp_evresulthome; +close(3);close(7);close(6); +A = euclid_dist(ncutv(:,2:6)); +A = exp(-A/(0.05^2)); + +[v,d] = eigs(A,9); + +figure(2); +for j=1:9,subplot(3,3,j);ims(v(:,j),nr,nc);colorbar;end + + +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_groups.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_groups.m new file mode 100755 index 0000000..d087218 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_groups.m @@ -0,0 +1,13 @@ +function disp_groups(groups,ids,nr,nc); + +np = ids(end); + +baseid =1; +for j=1:length(ids), + mask = zeros(np,1); + mask(groups(baseid:ids(j))) = 1+mask(groups(baseid:ids(j))); + + subplot(3,3,j); + ims(mask,nr,nc); + baseid = 1+ids(j); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_hist2d.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_hist2d.m new file mode 100755 index 0000000..4313234 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/disp_hist2d.m @@ -0,0 +1,15 @@ +function H2 = disp_hist2d(J,Jf,scales,filter_ids) + +ns = length(scales); +nf = length(filter_ids); + +H2 = []; +for j=1:ns, + for k=1:nf, + subplot(ns,nf,(j-1)*nf+k); + H2d = hist_I_f(J,Jf(:,:,filter_ids(k),scales(j))); + imagesc(H2d);axis('image');axis('off');drawnow; + H2(:,:,j,k) = H2d; + end +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair.m new file mode 100755 index 0000000..1c7b2cf --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair.m @@ -0,0 +1,28 @@ +function d = dist_pair(idx,fv,hb) +% (hb=sigs,bin_mins,bin_maxs,nbins) +% +% +% computes the pairwise distance between +% a point and everyone else using histogram binized feature +% + + +[nf,np] = size(fv); + +d = zeros(1,np); +nbins = [0,hb.nbins]; + + +for j=1:nf, + bin_min = hb.bmins(j); + bin_max = hb.bmaxs(j); + nbin = nbins(j+1); + sig = hb.sigs(j); + fprintf(sprintf('|%d',j)); + b = binize(fv(j,:),sig,bin_min,bin_max,nbin); + + a = binize(fv(j,idx),sig,bin_min,bin_max,nbin); + + d = d + a'*b; +end +fprintf('\n'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair_chank.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair_chank.m new file mode 100755 index 0000000..96a0d60 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/dist_pair_chank.m @@ -0,0 +1,25 @@ +function d = dist_pair_chank(a,fvs,chank_size) +% (hb=sigs,bin_mins,bin_maxs,nbins) +% +% +% computes the pairwise distance between +% a point and everyone else using histogram binized feature +% + + +[nf,np] = size(fvs); + +n_chanks = ceil(np/chank_size); + +d = []; +for j=1:n_chanks, + fprintf('<'); + + cm = sprintf('load st_%d',j); + eval(cm); + fprintf(sprintf('%d',n_chanks-j)); + + d = [d,a*fh]; +end + +fprintf('\n'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/doog2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/doog2.m new file mode 100755 index 0000000..3ec22c1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/doog2.m @@ -0,0 +1,43 @@ +function [G]=doog2(sig,r,th,N); +% [G,H]=doog2(sig,r,th,N); +% Make difference of offset gaussians kernel +% theta is in degrees +% (see Malik & Perona, J. Opt. Soc. Amer., 1990) +% +% Example: +% >> imagesc(-doog2(.5,4,15,32)) +% >> colormap(gray) + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid +pad_pts=no_pts*sqrt(2); % pad grid dimensions for up to a 45 degree rotation +siz=6; % range of x,y grid + +[x,y]=meshgrid(linspace(-siz,siz,pad_pts),linspace(-siz,siz,pad_pts)); + +a=-1; +b=2; +c=-1; + +ya=sig; +yc=-ya; +yb=0; +sigy=sig; +sigx=r*sig; + +Ga=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-ya)/sigy).^2)); +Gb=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-yb)/sigy).^2)); +Gc=(1/(2*pi*sigx*sigy))*exp(-(((x-0)/sigx).^2+((y-yc)/sigy).^2)); + +Go = a*Ga + b*Gb + c*Gc; +%Ho = imag(hilbert(Go)); +G = Go; + +G = mimrotate(Go,th,'bilinear','crop'); +G = imcrop(G,[(pad_pts-no_pts)/2, (pad_pts-no_pts)/2, no_pts, no_pts]); + +%H = imrotate(Ho,th,'bilinear','crop'); +%H = imcrop(H,[(pad_pts-no_pts)/2, (pad_pts-no_pts)/2, no_pts, no_pts]); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp.m new file mode 100755 index 0000000..5209088 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp.m @@ -0,0 +1,15 @@ +function [v,d] = eig_decomp(A) + +ds = sum(A); +ds = ones(size(ds))./sqrt(ds); +D1 = ds'*ones(1,length(ds)); +A = D1'.*A.*D1; + +disp(sprintf('computing eig values')); +tic;[v,d] = eig(A);toc; + +d = abs(diag(d)); +[tmp,idx] = sort(-d); +d = d(idx); +v = v(:,idx); +v = D1.*v; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp_v5.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp_v5.m new file mode 100755 index 0000000..e0fab2c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_decomp_v5.m @@ -0,0 +1,13 @@ +function [v,d] = eig_decomp_v5(A,nv) + +ds = sum(A); +ds = ones(size(ds))./sqrt(ds); +D1 = ds'*ones(1,length(ds)); +A = D1'.*A.*D1; + +disp(sprintf('computing eig values')); +tic;[v,d] = eigs(A,nv);toc; + +d = abs(diag(d)); + +v = D1(:,1:size(v,2)).*v; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_proj.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_proj.m new file mode 100755 index 0000000..78d5296 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eig_proj.m @@ -0,0 +1,12 @@ +function v = eig_proj(u,data) + +% fd = feature dimension, nv = num. of eigvectors +[fd,nv] = size(u); + +[fd2,nd] = size(data); + +if (fd ~= fd2), + error(sprintf('size don't match')); +else + v = data'*u; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eigs_decomp.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eigs_decomp.m new file mode 100755 index 0000000..7763124 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/eigs_decomp.m @@ -0,0 +1,39 @@ +function [v,d,D,Ipara] = eigs_decomp(fn,num_eigs) +% +% function [v,d,D,Ipara] = eigs_decomp(fn,num_eigs) +% + +%fn = '2.ppm'; +fn = 'images/130049.pgm'; + + +% spatial gaussian parameter +xscale = 3; + +% half size of the neighbourhood +xnb = 6; + +% setting the the HSV gaussian parameter:[h s v] +Iscale = [0.008,0.01,0.01]; + +Input_para = [xscale,xnb,Iscale]; + +% compute the lower half the association matrix +[A,D,Ipara] = compute_A_ppm(fn,Input_para); + +B = A+A'; +clear A; + +% eigen decompostion +options.tol = 1e-7; +num_eig_v = 4; +fprintf('doing eigs ...\n'); +[v,d] = eigs(B,num_eig_v,options); + +d = diag(d); + +% to display the final result + +%nr = Ipara(1);nc = Ipara(2); +%k = 1;imagesc(reshape(v(:,k).*D,nc,nr)');colorbar + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/euclid_dist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/euclid_dist.m new file mode 100755 index 0000000..cbf0734 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/euclid_dist.m @@ -0,0 +1,22 @@ +function [A,mag] = euclid_dist(v) + + + +A = 2*v*v'; + +nv = size(v,2); +if (nv>1) + mag = sum((v.*v)')'; +else + mag = v.*v; +end + +np = length(mag); + +for j=1:np, + A(:,j) = mag-A(:,j); +end + +for j=1:np, + A(j,:) = mag' + A(j,:); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_all_files.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_all_files.m new file mode 100755 index 0000000..c1752e5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_all_files.m @@ -0,0 +1,29 @@ +sigs = [1/sqrt(2),1,sqrt(2),2,2*sqrt(2)];r = 3;szs = round(r*3*sigs); + +load filenames; + +nfiles = size(filename,1); + +for j = 48:nfiles, + fname = ['images/',filename(j,:)]; + fname + I = readpgm(fname); + + text_des = compute_filter(I,sigs,r,szs); + + data_name = sprintf('filter_%s.mat',filename(j,:)); + cm = sprintf('save %s ',data_name); + + disp(cm); + eval(cm); + clear; + + sigs = [1/sqrt(2),1,sqrt(2),2,2*sqrt(2)];r = 3;szs = round(r*3*sigs); + + load filenames; + + nfiles = size(filename,1); + + +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_output.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_output.m new file mode 100755 index 0000000..a489c6e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/filter_output.m @@ -0,0 +1,38 @@ +function If = filter_output(I,sigs,szs,flag); +% +% compute filter output for all orientation and scale, +% + +%% flag = 1 if compute oriented filter output +if (~exist('flag')), + flag = 1; +end + + +If = []; + +for j = 1:length(sigs), + sig = sigs(j); + sz = 2*round(4*sig)+1; + + g = mkdog1(sig,sz); + fprintf('['); + fprintf('.'); + If(:,:,1,j) = conv2(I,g,'same'); + + angles = [0:30:150]; + r = 3; + + if flag, + for k = 1:length(angles), + fprintf('.'); + g = mdoog2(sig,r,angles(k),szs(j)); + If(:,:,k+1,j) = conv2(I,g,'same'); + end + end + + fprintf(']'); + +end + +fprintf('\n'); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_bst_cut.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_bst_cut.m new file mode 100755 index 0000000..c85cc2b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_bst_cut.m @@ -0,0 +1,24 @@ +function [cut_threshold,max_asso] = find_bst_cut(fn_base,para,threshold,Gmask) + +basedir = 'plaatje_data/'; +%basedir = './'; + +fn = sprintf('%sbst_cut.tex',basedir); +write_command(fn,fn_base,para); + +fn= sprintf('%sthreshold_%s.pfm',basedir,fn_base); +writepfm(fn,threshold(:)); + +fn= sprintf('%sGmask_%s.pfm',basedir,fn_base); +writepfm(fn,Gmask(:)); + +cd plaatje_data +!./find_bestcut +cd /home/barad-dur/vision/malik/jshi/proj/grouping/texture + +fn = sprintf('%sbst_asso_%s.pfm',basedir,fn_base); +results = readpfm(fn); +asso = results(1,:); +[max_asso,id] = max(asso); +cut_threshold = threshold(id); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_center.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_center.m new file mode 100755 index 0000000..b5a127a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_center.m @@ -0,0 +1,4 @@ +function [center_x,center_y] = find_center(size_x,size_y); + +center_x = 0.5*(size_x -1)+1; +center_y = 0.5*(size_y -1)+1; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_cutpoint.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_cutpoint.m new file mode 100755 index 0000000..5cab956 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/find_cutpoint.m @@ -0,0 +1,13 @@ +function [cutpoints,x] = find_cutpoint(data,cmap,nbin) +% +% [cutpoints,x] = find_cutpoint(data,cmap,nbin) +% +% + +x = id_cut(data,cmap,nbin); + +cutpoints = zeros(1,nbin); + +for j=1:nbin, + cutpoints(j) = max(data(x<=j)); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/gen_filters.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/gen_filters.m new file mode 100755 index 0000000..65e3c3a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/gen_filters.m @@ -0,0 +1,47 @@ +function filters = gen_filters(sig,r,sz); + + +as = 0:30:150; + +filters = []; + +if size(sig,2)== 1, + + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + + g = g - mean(reshape(g,prod(size(g)),1)); + + g = g/sum(sum(abs(g))); + + filters(:,:,j) = g; + end +else + + % there are multiple scales + sigs = sig; + szs = sz; + for k = 1:size(sigs,2), + sig = sigs(k); + sz = szs(length(szs)-1); + fprintf('%d',k); + for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = mdoog2(sig,r,angle,round(sz)); + g = g - mean(reshape(g,prod(size(g)),1)); + g = g/sum(sum(abs(g))); + + filters(:,:,j,k) = g; + end + + + end + +end + +fprintf('\n'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist.m new file mode 100755 index 0000000..b05d680 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist.m @@ -0,0 +1,9 @@ +function cumhists = get_cumhist(hists) +% +% +% cumhists = get_cumhist(hists) +% + +cumhists.inten = cumsum(hists.inten)/sum(hists.inten); +cumhists.text = cumsum(hists.text,1)./(ones(size(hists.text,1),1)*sum(hists.text,1)); +cumhists.mag = cumsum(hists.mag)/sum(hists.mag); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist_inten.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist_inten.m new file mode 100755 index 0000000..911b4e6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_cumhist_inten.m @@ -0,0 +1,7 @@ +function CH_inten = get_cumhist(hists) +% +% +% cumhists = get_cumhist(hists) +% + +CH_inten = cumsum(hists)/sum(hists); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist.m new file mode 100755 index 0000000..d480a3a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist.m @@ -0,0 +1,24 @@ +function [hists,bins] = get_hists(J,Jbar) +% +% +% produce histogram output of the image J and its +% filter outputs Jbar +% + +maxval = 60; +bin = [1:4:maxval+1]; + +w = size(J); + + +[hists.inten,bins.inten] = hist(reshape(J,prod(w),1),[1:26:256]); + +for j=1:size(Jbar,3), + hists.text(:,j) = hist(reshape(abs(Jbar(:,:,j)),prod(w),1),bin); +end + +bins.text = bin; + +[hists.mag,bins.mag] = hist(reshape(sum(abs(Jbar),3),prod(w),1),[1:10:161]); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist_inten.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist_inten.m new file mode 100755 index 0000000..c8357c6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_hist_inten.m @@ -0,0 +1,15 @@ +function [Hinten,Hbins] = get_hists_inten(J,nbin) +% +% +% produce histogram output of the image J and its +% filter outputs Jbar +% + + +w = size(J); + +[Hinten,Hbins] = hist(reshape(J,prod(w),1),linspace(1,256,nbin)); + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win.m new file mode 100755 index 0000000..411a694 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win.m @@ -0,0 +1,10 @@ +function J = get_win(I,center,wc) +% +% J = get_win(I,center,wc) +% +% center: [x,y] + + + +J = I(center(2)-wc(2):center(2)+wc(2),... + center(1)-wc(1):center(1)+wc(1)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win5.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win5.m new file mode 100755 index 0000000..e8404e5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/get_win5.m @@ -0,0 +1,11 @@ +function J = get_win5(I,center,wc) +% +% J = get_win5(I,center,wc) +% +% center: [x,y] + + + +J = I(center(2)-wc(2):center(2)+wc(2),... + center(1)-wc(1):center(1)+wc(1),:,:); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/grad.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/grad.m new file mode 100755 index 0000000..6da0fbf --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/grad.m @@ -0,0 +1,24 @@ +% gradient of an image +% coordinates (r, c) follow matrix convention; +% the gaussian is truncated at x = +- tail, and there are samples samples +% inbetween, where samples = hsamples * 2 + 1 + +function[gr,gc] = gradient(image, hsamples) + +tail=4; +samples = hsamples * 2 + 1; + +x = linspace(-tail, tail, samples); +gauss = exp(-x.^2); +n = gauss * ones(samples,1); +gauss = gauss/n; + +gaussderiv = -x.*gauss; +n = -gaussderiv*linspace(1,samples,samples)'; +gaussderiv = gaussderiv/n; + +gr = conv2(conv2(image, gaussderiv','valid'), gauss,'valid'); +gc = conv2(conv2(image, gaussderiv,'valid'), gauss','valid'); + +%gr = conv_trim(gr, hsamples, hsamples); +%gc = conv_trim(gc, hsamples, hsamples); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/half_sigmoid.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/half_sigmoid.m new file mode 100755 index 0000000..c187b6c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/half_sigmoid.m @@ -0,0 +1,17 @@ +function a = half_sigmoid(x,offset,sig) +% +% a = half_sigmoid(x,offset,sig) +% +% a = ones(size(x))./(1+exp(-(x-offset)/sig)); +% +% keep the sign of a + +sign_x = sign(x); +x = abs(x); + +a = ones(size(x))./(1+exp(-(x-offset)/sig)); + +off = 1/(1+exp(-(0-offset)/sig)); + +a = sign_x.*(a-off); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist2d.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist2d.m new file mode 100755 index 0000000..3f4db0c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist2d.m @@ -0,0 +1,13 @@ +function H2 = hist2d(J,Jf,scales,filter_ids) + +ns = length(scales); +nf = length(filter_ids); + +H2 = []; +for j=1:ns, + for k=1:nf, + H2d = hist_I_f(J,Jf(:,:,filter_ids(k),scales(j))); + H2(:,:,j,k) = H2d; + end +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_I_f.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_I_f.m new file mode 100755 index 0000000..a993661 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_I_f.m @@ -0,0 +1,22 @@ +function h2d = hist_I_f(I,If,binI,binf) + +if (nargin == 2), + binI = [0:13:260]; + binf = [-30:2.5*2:30]; +end + +%%% make 2d histogram bin +h2d = []; + +for j = 2:length(binf), + + [id_i,id_j] = find((If>binf(j-1)) & (If<=binf(j))); + if (length(id_i) >0), + h = hist(I(id_i+(id_j-1)*size(I,1)),binI); + else + h = zeros(size(binI)); + end + + h2d = [h2d,h']; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_diff.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_diff.m new file mode 100755 index 0000000..6976c8f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_diff.m @@ -0,0 +1,30 @@ +function hdiff = hist_diff(H1,H2) +% +% hdiff = hist_diff(H1,H2) +% + +ns = size(H1,3); +nf = size(H1,4); + +sI= [1,0,1];sI = exp(-sI); +sI = sI/sum(sI); + +hdiff = 0; +for j = 1:ns, + for k = 1:nf, + h1 = H1(:,:,j,k); + h2 = H2(:,:,j,k); + + h1s = conv2(conv2(h1,sI','same'),sI,'same'); + h2s = conv2(conv2(h2,sI','same'),sI,'same'); + + [is,js] = find( (h1>0) | (h2>0)); + ids = (js-1)*size(h1,1) + is; + + xdiffs = ((h1s(ids)-h2s(ids)).*(h1s(ids)-h2s(ids)))./(h1s(ids)+h2s(ids)); + hdiff = hdiff + sum(xdiffs); + + end +end + +hdiff = hdiff/(ns*nf); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_f.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_f.m new file mode 100755 index 0000000..93f50a9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_f.m @@ -0,0 +1,28 @@ +function h2d = hist_f(Ifs,f1,s1,f2,s2) + + +binf = [-30:2.5*2:30]; + + +%%% make 2d histogram bin + +If1 = Ifs(:,:,f1,s1); +If2 = Ifs(:,:,f2,s2); +h2d = []; + +binf(1) = -100; +binf(length(binf)) = 100; + +for j = 2:length(binf), + + [id_i,id_j] = find((If1>binf(j-1)) & (If1<=binf(j))); + if (length(id_i) >0), + + h = hist(If2(id_i+(id_j-1)*size(If2,1)),binf); + else + h = zeros(size(binf)); + end + + + h2d = [h2d,h']; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_in_chank.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_in_chank.m new file mode 100755 index 0000000..74661b2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_in_chank.m @@ -0,0 +1,33 @@ +function covfh = hist_inner_chank(fv,chank_size,nbin) +% fh = hist_inner_chank(fv,hb,chank_file) +% +% (hb = bin_mins,bin_maxs,nbins) +% +% take which histogram value and turn it into histogram bin +% compute the inner product of the histogram bin features +% + +[nf,np] = size(fv); + +tbins = nf*nbin; +disp(sprintf('need matrix of %d x %d ',tbins,tbins)); + +covfh = zeros(tbins,tbins); + +n_chanks = ceil(np/chank_size); +for j=1:n_chanks, + fprintf('<'); + + cm = sprintf('load st_%d',j); + eval(cm); + fprintf(sprintf('%d',n_chanks-j)); + + %ms = mean(fh'); + %fh = fh- ms'*ones(1,size(fh,2)); + + covfh = covfh + fh*fh'; + fprintf('>'); +end + +fprintf('\n'); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_inner.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_inner.m new file mode 100755 index 0000000..6fd02b7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hist_inner.m @@ -0,0 +1,40 @@ +function fh = hist_inner(fv,hb) +% (hb = bin_mins,bin_maxs,nbins) +% +% take which histogram value and turn it into histogram bin +% compute the inner product of the histogram bin features +% + +[nf,np] = size(fv); + +nbins = [0,hb.nbins]; + +disp(sprintf('need matrix of %d x %d ',sum(nbins),sum(nbins))); + +fh = zeros(sum(nbins),sum(nbins)); + +for j=1:nf, + bin_min = hb.bmins(j); + bin_max = hb.bmaxs(j); + nbin = nbins(j+1); + sig = hb.sigs(j); + fprintf('|'); + b0 = binize(fv(j,:),sig,bin_min,bin_max,nbin); + + fh(sum(nbins(1:j))+1:sum(nbins(1:j+1)),sum(nbins(1:j))+1:sum(nbins(1:j+1))) = b0*b0'; + + for k=j+1:nf, + bin_min = hb.bmins(k); + bin_max = hb.bmaxs(k); + nbin = nbins(k+1); + sig = hb.sigs(k); + fprintf('.'); + b = binize(fv(k,:),sig,bin_min,bin_max,nbin); + tmp = b0*b'; + + fh(sum(nbins(1:j))+1:sum(nbins(1:j+1)),sum(nbins(1:k))+1:sum(nbins(1:k+1))) = tmp; + fh(sum(nbins(1:k))+1:sum(nbins(1:k+1)),sum(nbins(1:j))+1:sum(nbins(1:j+1))) = tmp'; + end +end + +fprintf('\n'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/histbin_fv_chank.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/histbin_fv_chank.m new file mode 100755 index 0000000..8cf2a35 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/histbin_fv_chank.m @@ -0,0 +1,14 @@ +function histbin_fv_chank(fvs,hb,chank_size,fname_base) + +[nv,np] = size(fvs); + +k =1; +for j=1:chank_size:np, + disp(sprintf('|%d',j)); + fh = colize_hist(fvs(:,j:min(j+chank_size-1,np)),hb); + fname = sprintf('%s_%d.mat',fname_base,k); + cm = sprintf('save %s fh hb;',fname); + disp(cm); + eval(cm); + k = k+1; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hsv2clrs.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hsv2clrs.m new file mode 100755 index 0000000..cfbb8b0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/hsv2clrs.m @@ -0,0 +1,25 @@ +function [x,y,z] = hsv2clrs(h,s,v) +% +% function [x,y,z] = hsv2clrs(h,s,v) +% if h is 3D matrix, output in 3D x +% + +if (size(h,3) == 3), + s = h(:,:,2); + v = h(:,:,3); + h = h(:,:,1); + + z = v; + xx = s.*v.*cos(2*pi*h); + y = s.*v.*sin(2*pi*h); + + x(:,:,1) = xx; + x(:,:,2) = y; + x(:,:,3) = z; +else + + z = v; + x = s.*v.*cos(2*pi*h); + y = s.*v.*sin(2*pi*h); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/id_cut.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/id_cut.m new file mode 100755 index 0000000..daf8f2b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/id_cut.m @@ -0,0 +1,14 @@ +function [x,map] = idcut(data,cmap,nbin) +% +% +% + +lc = size(cmap,1); + +data = data - min(data); +data = 1+ ((lc-1)*data/max(data)); + +r = cmap(data,1);g = cmap(data,2);b = cmap(data,3); + +[x,map] = vmquant(r,g,b,nbin); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im.m new file mode 100755 index 0000000..6450120 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im.m @@ -0,0 +1,3 @@ +function im(I) + +imagesc(I);axis('image');drawnow; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im3.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im3.m new file mode 100755 index 0000000..b49e690 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im3.m @@ -0,0 +1,3 @@ +function im3(d) + +imagesc(reshape(d,size(d,1),size(d,2)*size(d,3))); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im5.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im5.m new file mode 100755 index 0000000..7f1b16d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im5.m @@ -0,0 +1,16 @@ +function im5(data,nr,nc,mag) + +if nargin == 4, +for j=1:size(data,3), + subplot(nr,nc,j); + imagesc(data(:,:,j)./mag);axis('image');axis('off');colorbar;drawnow; + +% image(150*data(:,:,j));axis('image');axis('off');colorbar;drawnow; +end + +else +for j=1:size(data,3), + subplot(nr,nc,j); + imagesc(data(:,:,j));axis('image');axis('off');colorbar;drawnow; +end +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im_vect.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im_vect.m new file mode 100755 index 0000000..d0a5b30 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/im_vect.m @@ -0,0 +1,20 @@ +function a = im_vect(loca,v,scale); + +if ~exist('scale'), + scale = 50; +end + +y = loca(1,:); +x = loca(2,:); + +x = x - min(x); +y = y - min(y); + +max_x = max(x);max_y = max(y); +min_scale = min(max_x,max_y); + +x = scale*x/min_scale; +y = scale*y/min_scale; + + +a = sparse(y+1,x+1,v); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/imrotate.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/imrotate.m new file mode 100755 index 0000000..167fd02 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/imrotate.m @@ -0,0 +1,119 @@ +function bout = imrotate(arg1,arg2,arg3,arg4) +%IMROTATE Rotate image. +% B = IMROTATE(A,ANGLE,'method') rotates the image A by ANGLE +% degrees. The image returned B will, in general, be larger +% than A. Invalid values on the periphery are set to one +% for indexed images or zero for all other image types. Possible +% interpolation methods are 'nearest','bilinear' or 'bicubic'. +% 'bilinear' is the default for intensity images, otherwise +% 'nearest' is used if no method is given. +% +% B = IMROTATE(A,ANGLE,'crop') or IMROTATE(A,ANGLE,'method','crop') +% crops B to be the same size as A. +% +% Without output arguments, IMROTATE(...) displays the rotated +% image in the current axis. +% +% See also IMRESIZE, IMCROP, ROT90. + +% Clay M. Thompson 8-4-92 +% Copyright (c) 1992 by The MathWorks, Inc. +% $Revision: 1.14 $ $Date: 1993/09/01 21:27:38 $ + +if nargin<2, error('Requires at least two input parameters.'); end +if nargin<3, + if isgray(arg1), caseid = 'bil'; else caseid = 'nea'; end + docrop = 0; +elseif nargin==3, + if isstr(arg3), + method = [lower(arg3),' ']; % Protect against short method + caseid = method(1:3); + if caseid(1)=='c', % Crop string + if isgray(arg1), caseid = 'bil'; else caseid = 'nea'; end + docrop = 1; + else + docrop = 0; + end + else + error('''METHOD'' must be a string of at least three characters.'); + end +else + if isstr(arg3), + method = [lower(arg3),' ']; % Protect against short method + caseid = method(1:3); + else + error('''METHOD'' must be a string of at least three characters.'); + end + docrop = 1; +end + +% Catch and speed up 90 degree rotations +if rem(arg2,90)==0 & nargin<4, + phi = rem(arg2,360); + if phi==90, + b = rot90(arg1); + elseif phi==180, + b = rot90(arg1,2); + elseif phi==270, + b = rot90(arg1,-1); + else + b = arg1; + end + if nargout==0, imshow(b), else bout = b; end + return +end + +phi = arg2*pi/180; % Convert to radians + +% Rotation matrix +T = [cos(phi) -sin(phi); sin(phi) cos(phi)]; + +% Coordinates from center of A +[m,n] = size(arg1); +if ~docrop, % Determine limits for rotated image + siz = ceil(max(abs([(n-1)/2 -(m-1)/2;(n-1)/2 (m-1)/2]*T))/2)*2; + uu = -siz(1):siz(1); vv = -siz(2):siz(2); +else % Cropped image + uu = (1:n)-(n+1)/2; vv = (1:m)-(m+1)/2; +end +nu = length(uu); nv = length(vv); + +blk = bestblk([nv nu]); +nblks = floor([nv nu]./blk); nrem = [nv nu] - nblks.*blk; +mblocks = nblks(1); nblocks = nblks(2); +mb = blk(1); nb = blk(2); + +rows = 1:blk(1); b = zeros(nv,nu); +for i=0:mblocks, + if i==mblocks, rows = (1:nrem(1)); end + for j=0:nblocks, + if j==0, cols = 1:blk(2); elseif j==nblocks, cols=(1:nrem(2)); end + if ~isempty(rows) & ~isempty(cols) + [u,v] = meshgrid(uu(j*nb+cols),vv(i*mb+rows)); + % Rotate points + uv = [u(:) v(:)]*T'; % Rotate points + u(:) = uv(:,1)+(n+1)/2; v(:) = uv(:,2)+(m+1)/2; + if caseid(1)=='n', % Nearest neighbor interpolation + b(i*mb+rows,j*nb+cols) = interp6(arg1,u,v); + elseif all(caseid=='bil'), % Bilinear interpolation + b(i*mb+rows,j*nb+cols) = interp4(arg1,u,v); + elseif all(caseid=='bic'), % Bicubic interpolation + b(i*mb+rows,j*nb+cols) = interp5(arg1,u,v); + else + error(['Unknown interpolation method: ',method]); + end + end + end +end + +d = find(isnan(b)); +if length(d)>0, + if isind(arg1), b(d) = ones(size(d)); else b(d) = zeros(size(d)); end +end + +if nargout==0, + imshow(b), return +end +bout = b; + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ims.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ims.m new file mode 100755 index 0000000..2fb5f25 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ims.m @@ -0,0 +1,3 @@ +function ims(I,nr,nc) + +im(reshape(I,nr,nc)); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/imvs.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/imvs.m new file mode 100755 index 0000000..21dde73 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/imvs.m @@ -0,0 +1,4 @@ +function imvs(I,v,nr,nc) + +v = reshape(v,nr,nc); +im(I(1:size(v,1),1:size(v,2)).*v); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/is_step.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/is_step.m new file mode 100755 index 0000000..4903778 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/is_step.m @@ -0,0 +1,33 @@ +function P_step = is_step(gx,gy) +% +% P_step = is_step(gx,gy) +% +% determine wheter gx,gy(which is first +% order filter output) is a response +% to a step function +% + +M = zeros(2,2); +M(1,1) = sum(sum(gx.*gx)); +M(2,2) = sum(sum(gy.*gy)); +M(1,2) = sum(sum(gx.*gy)); +M(2,1) = M(1,2); + +[v,d] = eig(M); +d = diag(d); + +% make the first eig_value to be the smaller one +if (d(2)< d(1)), + tmp = d(1);d(1) = d(2);d(2) = tmp; + tmp = v(:,1); v(:,1) = v(:,2); v(:,2) = tmp; +end + +ratio = d(1)/d(2); +threshold = 0; +gx_ratio = sum(sum(gx.*(gx>threshold)))/(sum(sum(abs(gx).*(abs(gx)>threshold)))); +gx_ratio = max(gx_ratio,1-gx_ratio); + +gy_ratio = sum(sum(gy.*(gy>threshold)))/(sum(sum(abs(gy).*(abs(gy)>threshold)))); +gy_ratio = max(gy_ratio,1-gy_ratio); + +P_step = [ratio,gx_ratio,gy_ratio]; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ks_2d.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ks_2d.m new file mode 100755 index 0000000..cfac005 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ks_2d.m @@ -0,0 +1,20 @@ +function [na,nb,nc,nd] = ks_2d(cum_filter) + +[nb_filters,nb_bins] = size(cum_filter); + +T = 1; + +for j = [1:nb_filters], + for l = [1:nb_bins], + nc(j,l) = sum(cum_filter(1:j,l)); + nd(j,l) = j*T - nc(j,l); + + if (j~= nb_filters), + nb(j,l) = sum(cum_filter(j+1:nb_filters,l)); + na(j,l) = (nb_filters-j)*T-nb(j,l); + else + nb(j,l) = 0; + na(j,l) = 0; + end + end +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/load_result.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/load_result.m new file mode 100755 index 0000000..0ff328c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/load_result.m @@ -0,0 +1,39 @@ + +fnameI = '130056'; + +cm = sprintf('load filter_%s.pgm.mat',fnameI); +disp(cm); +eval(cm); + +text_des= reshape(text_des,size(text_des,1),size(text_des,2),size(text_des,3)*size(text_des,4)); + +for j=1:size(text_des,3), + text_des(:,:,j) = abs(text_des(:,:,j)).*(text_des(:,:,j) <0); +end + +%text_des = abs(text_des); + + + %%%% cutoff margins, +margin = 6+10; + +Iw = cutoff(I,margin); + + +T1 = cutoff(text_des,margin); + +%%%%% reduce resolution + + + +T1 = reduce_all(T1); +T1 = reduce_all(T1); + +im5(T1,5,6); + +cm = sprintf('writepnm5(''%s_f.pnm'',%s)',fnameI,'T1/70'); + +% disp(cm);eval(cm); + +nr = size(T1,1); +nc = size(T1,2); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/m_interp4.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/m_interp4.m new file mode 100755 index 0000000..314f140 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/m_interp4.m @@ -0,0 +1,49 @@ +function [F,mask] = m_interp4(z,s,t) +%INTERP4 2-D bilinear data interpolation. +% ZI = INTERP4(Z,XI,YI) assumes X = 1:N and Y = 1:M, where +% [M,N] = SIZE(Z). +% +% Copyright (c) 1984-93 by The MathWorks, Inc. +% Clay M. Thompson 4-26-91, revised 7-3-91, 3-22-93 by CMT. +% +% modified to + + +[nrows,ncols] = size(z); + + +if any(size(z)<[3 3]), error('Z must be at least 3-by-3.'); end +if size(s)~=size(t), error('XI and YI must be the same size.'); end + +% Check for out of range values of s and set to 1 +sout = find((s<1)|(s>ncols)); +if length(sout)>0, s(sout) = ones(size(sout)); end + +% Check for out of range values of t and set to 1 +tout = find((t<1)|(t>nrows)); +if length(tout)>0, t(tout) = ones(size(tout)); end + +% Matrix element indexing +ndx = floor(t)+floor(s-1)*nrows; + +% Compute intepolation parameters, check for boundary value. +d = find(s==ncols); +s(:) = (s - floor(s)); +if length(d)>0, s(d) = s(d)+1; ndx(d) = ndx(d)-nrows; end + +% Compute intepolation parameters, check for boundary value. +d = find(t==nrows); +t(:) = (t - floor(t)); +if length(d)>0, t(d) = t(d)+1; ndx(d) = ndx(d)-1; end +d = []; + +% Now interpolate, reuse u and v to save memory. +F = ( z(ndx).*(1-t) + z(ndx+1).*t ).*(1-s) + ... + ( z(ndx+nrows).*(1-t) + z(ndx+(nrows+1)).*t ).*s; + +mask = ones(size(z)); + +% Now set out of range values to zeros. +if length(sout)>0, F(sout) = zeros(size(sout));mask(sout)=zeros(size(sout));end +if length(tout)>0, F(tout) = zeros(size(tout));mask(tout)=zeros(size(tout));end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/make_masks.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/make_masks.m new file mode 100755 index 0000000..d2c8ba7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/make_masks.m @@ -0,0 +1,12 @@ +function masks = make_masks(groups,ids,nr,nc); + +np = ids(end); + +baseid =1; +for j=1:length(ids), + mask = zeros(np,1); + mask(groups(baseid:ids(j))) = 1+mask(groups(baseid:ids(j))); + baseid = 1+ids(j); + + masks(:,:,j) = mask; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/makefilter.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/makefilter.m new file mode 100755 index 0000000..a768269 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/makefilter.m @@ -0,0 +1,14 @@ +function H = make_filter(txture,sze,noise) +% function H = make_filter(txture,sze) +% +% + +x = zeros(sze); +tx_sze = size(txture); + +x(1:tx_sze(1),1:tx_sze(2)) = txture; +x = reshape(x,1,sze(1)*sze(2)); +X = fft(x); +figure(3);plot(abs(X).^2); + +H = X./(abs(X).^2+noise); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t1.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t1.m new file mode 100755 index 0000000..256e609 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t1.m @@ -0,0 +1,22 @@ +function [H,h] = make_filter(txture,sze,noise) +% function H = make_filter(txture,sze) +% +% + +tx_sze = size(txture); +x = reshape(txture,1,tx_sze(1)*tx_sze(2)); +X = fft(x); +H = X./(abs(X).^2+noise); +h = reshape(real(ifft(H)),tx_sze(1),tx_sze(2)); + + +x = zeros(sze); +x(1:tx_sze(1),1:tx_sze(2)) = h; +figure(1);imagesc(x);drawnow; +x = reshape(x,1,sze(1)*sze(2)); +H = fft(x); + +h = reshape(real(ifft(H)),sze(1),sze(2)); +figure(2);imagesc(h); + +figure(3); mesh(h); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t2.m new file mode 100755 index 0000000..c84482d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_t2.m @@ -0,0 +1,21 @@ +function [H,h] = make_filter(txture,sze,noise) +% function H = make_filter(txture,sze) +% +% + +x = zeros(sze); +tx_sze = size(txture); + +[center_x,center_y] = find_center(sze(2),sze(1)); +tx_sze_h = round(0.5*tx_sze); + +x(center_y-tx_sze_h(1):center_y-tx_sze_h(1)+tx_sze(1)-1,... + center_x-tx_sze_h(2):center_x-tx_sze_h(2)+tx_sze(2)-1) = txture; +figure(1);imagesc(x);drawnow; +x = reshape(x,1,sze(1)*sze(2)); +X = fft(x); +H = X./(abs(X).^2+noise); +h = reshape(real(ifft(H)),sze(1),sze(2)); +figure(2);imagesc(h); + +figure(3); mesh(h); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_test.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_test.m new file mode 100755 index 0000000..e8d0ad8 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkf_test.m @@ -0,0 +1,43 @@ +function [H,h] = make_filter(txture,sze,n_c,a,b,c) +% function H = make_filter(txture,sze) +% +% + +x = zeros(sze); +tx_sze = size(txture); + +x(1:tx_sze(1),1:tx_sze(2)) = txture; +%figure(1);imagesc(x);drawnow; +x = reshape(x,1,sze(1)*sze(2)); +X = fft(x); +power = abs(X).^2; + +figure(3);plot(power); +len = length(X); + +t = [1:0.5*(length(X)-1),0.5*(length(X)-1):-1:1]; + +top = max(power); +if (c == -1), + c = top*5.0e-1 +end +if (n_c == -1), + n_c = top*1.0e-1 +end + +nois = n_c +c*(exp(-a*(t.^b))); +if (rem(len,2) == 1), + noise = [c+n_c,nois]; +else + noise = [c+n_c,nois,c+n_c]; +end + +figure(3);hold on;plot(noise,'r'); +hold off +H = X./(abs(X).^2+noise); +h = reshape(real(ifft(H)),sze(1),sze(2)); +figure(2);imagesc(h); + +figure(4);plot(abs(H).^2,'c') +%figure(3); mesh(h); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkg.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkg.m new file mode 100755 index 0000000..1fb1f7e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkg.m @@ -0,0 +1,9 @@ +function g= mkgaussian(xo,yo,sigma_x,sigma_y,size_w) +% +% function G = mkgaussian(xo,yo,sigma_x,sigma_y,size_w) +% + +size_wh = round(0.5*size_w); +[x,y] = meshgrid([-size_wh:1:size_wh],[-size_wh:1:size_wh]); +g = 1/(2*pi*sigma_x*sigma_y)*(exp(-( ((x-xo)/sigma_x).^2 + ((y-yo)/sigma_y).^2))); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkgaussian.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkgaussian.m new file mode 100755 index 0000000..1213757 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkgaussian.m @@ -0,0 +1,11 @@ +function G = mkgaussian(xo,yo,sigma_x,sigma_y,size_w) +% +% function G = mkgaussian(xo,yo,sigma_x,sigma_y,size_w) +% + +size_wh = round(0.5*size_w); + +[x,y] = meshgrid([-size_wh:1:size_wh],[-size_wh:1:size_wh]); + +G = 0.5/(sigma_x*sigma_y)*(exp(-( ((x-xo)/sigma_x).^2 + ((y-yo)/sigma).^2))); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkmulfilter.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkmulfilter.m new file mode 100755 index 0000000..fe501b5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkmulfilter.m @@ -0,0 +1,52 @@ +function H = make_multi_filter(templates,num_templates,sze,u,noise) +% function H = make_filter(templates,num_templates,sze,u,noise) +% templates are column vectors of template +% sze is the size of the filter +% u is a vector of specified value +% +% + +templates_size_x = size(templates,2)/num_templates; +templates_size_y = size(templates,1); + +alpha = 1/num_templates; + +X = zeros(num_templates,sze(1)*sze(2)); +Spectrums = zeros(num_templates,sze(1)*sze(2)); + + +for k =1:num_templates, + tmp = zeros(sze); + tmp(1:templates_size_y,1:templates_size_x) =... + templates(:,(k-1)*templates_size_x+1:k*templates_size_x); + x(k,:) = reshape(tmp,1,sze(1)*sze(2)); + X(k,:) = fft(x(k,:)); + Spectrums(k,:) = conj(X(k,:)).*X(k,:); +end + +if num_templates > 1 + sum_Spect = sum(Spectrums)*alpha; +else + sum_Spect = Spectrums; +end + +for row = 1:num_templates, + for column = 1:num_templates, + + A(row,column) = sum( ((conj(X(row,:)).*X(column,:))./... + (sum_Spect + noise))')/(sze(1)*sze(2)); + end +end + + +epsilon = inv(A)*u; + +top = epsilon(1)*X(1,:); +for k = 2:num_templates, + top = top + epsilon(k)*X(k,:); +end + +H = top./(sum_Spect + noise); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkpoog2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkpoog2.m new file mode 100755 index 0000000..4bd0366 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mkpoog2.m @@ -0,0 +1,29 @@ +function doog2 = mkdoog2(sigma_w,r,theta,size_w) +% +% function doog2 = mkdoog2(sigma_w,r,theta,size_w) +% +% + +%scale_base = 2.8814; +scale_base = 2; + +a = -1*scale_base; +b = 2*scale_base; +c = -1*scale_base; + +sigma_x = r*sigma_w; +sigma_y = sigma_w; + +ya = sigma_w; +yc = -sigma_w; +yb = 0; + +doog2 = a*mkg(0,ya,sigma_x,sigma_y,size_w) +... + b*mkg(0,yb,sigma_x,sigma_y,size_w) +... + c*mkg(0,yc,sigma_x,sigma_y,size_w); + +figure(3);colormap(hsv); +subplot(1,3,1);mesh(a*mkg(0,ya,sigma_x,sigma_y,size_w)); +subplot(1,3,2);mesh(b*mkg(0,yb,sigma_x,sigma_y,size_w)); +subplot(1,3,3);mesh(c*mkg(0,yc,sigma_x,sigma_y,size_w)); +%doog2 = 255*5.1745*doog2; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mksgn.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mksgn.m new file mode 100755 index 0000000..15f947b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mksgn.m @@ -0,0 +1,10 @@ +function sgn = makesgn(sigma,sigma_x,sze) + + +size_wh = round(0.5*sze); +[x,y] = meshgrid([-size_wh:1:size_wh],[-size_wh:1:size_wh]); +steps = 1/(1+2*sigma_x); + +fx = -1*(x<=-sigma_x) + (x>=sigma_x) + steps*((x+sigma_x).*(x>-sigma_x).*(x-sigma_x).*(x<1)) + steps*(sigma_x-x).*(x=1); +sgn = fx.*(exp(- (y.*y)/(2*sigma))); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm.m new file mode 100755 index 0000000..cf68a01 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm.m @@ -0,0 +1,11 @@ +function I = read_pfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2) +I = fscanf(fid,'%f',[A(1),A(2)]); + + +I = I'; +size(I); +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm2.m new file mode 100755 index 0000000..c01b25b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mreadpfm2.m @@ -0,0 +1,9 @@ +function I = read_pfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2) +I = fscanf(fid,'%f',[A(1),A(2)]); + + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis.m new file mode 100755 index 0000000..95de8f6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis.m @@ -0,0 +1,16 @@ +function [l1,l2,phi] = mwis(gx,gy) +% +% [l1,l2,phi] = mwis(gx,gy) +% +% + +sgx = sum(sum(gx.*gx)); +sgxy = sum(sum(gx.*gy)); +sgy = sum(sum(gy.*gy)); + +tr = sgx + sgy; +v1 = 0.5*sqrt(tr*tr - 4*(sgx*sgy-sgxy*sgxy)); +l1 = real(0.5*tr+v1); +l2 = real(0.5*tr-v1); + +phi= 0.5*atan2(2*sgxy,sgx-sgy); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis_image.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis_image.m new file mode 100755 index 0000000..c8e8b66 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/mwis_image.m @@ -0,0 +1,25 @@ +function [l1,l2,phi] = mwis_image(gx,gy,hs) +% +% [l1,l2,phi] = mwis_image(gx,gy) +% +% + +if (~exist('hs')), + hs = 10; +end + + +sgx = gx.*gx; +sgxy = gx.*gy; +sgy = gy.*gy; + +ssgx = smooth(sgx,hs); +ssgxy = smooth(sgxy,hs); +ssgy = smooth(sgy,hs); + +tr = ssgx + ssgy; +v1 = 0.5*sqrt(tr.*tr - 4*(ssgx.*ssgy-ssgxy.*ssgxy)); +l1 = real(0.5*tr+v1); +l2 = real(0.5*tr-v1); + +phi= 0.5*atan2(2*sgxy,sgx-sgy); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/myinterp.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/myinterp.m new file mode 100755 index 0000000..6d8f055 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/myinterp.m @@ -0,0 +1,18 @@ +function y = myinterp(d,rate) +% + + +if (size(d,1)>1), + d = [d;d(1)]; +else + d = [d,d(1)]; +end + +lgn = length(d); + +x = 1:lgn; +xx = linspace(1,lgn,rate*lgn); + +y = interp1(x,d,xx,'linear'); + +y = y(1:(length(y)-1)); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ncut_b.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ncut_b.m new file mode 100755 index 0000000..c9eee35 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/ncut_b.m @@ -0,0 +1,25 @@ +function [v,d] = ncut(A,nv) + +ds = sum(A); +ds = ones(size(ds))./sqrt(ds); + +for j=1:size(A,1), + A(j,:) = A(j,:).*ds; +end + +for j=1:size(A,2); + A(:,j) = A(:,j).*ds'; +end + +%D1 = ds'*ones(1,length(ds)); +%A = D1'.*A.*D1; + +disp(sprintf('computing eig values')); +tic;[v,d] = eigs(A,nv);toc; + +d = abs(diag(d)); + +for j=1:nv, + v(:,j) = v(:,j).*ds'; +end +%v = D1(:,1:size(v,2)).*v; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/new_compute_J.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/new_compute_J.m new file mode 100755 index 0000000..f4e376a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/new_compute_J.m @@ -0,0 +1,32 @@ +function [JJ,mask] = compute_J(A,D,I,center,window_size_h) +%% function J = compute_J(A,D,I,center,window_size_h) +% + +[size_y,size_x] = size(I); + +center_x = center(1); +center_y = center(2); + +XX = meshgrid(1:size_x,1:size_y); +YY = meshgrid(1:size_y,1:size_x)'; +x = reshape(XX,size_x*size_y,1); +y = reshape(YY,size_x*size_y,1); +index(:,1) = x-center_x; +index(:,2) = y-center_y; + +position_new = A*index'+ [D(1),0;0,D(2)]*ones(2,size_x*size_y); +position_new(1,:) = position_new(1,:)+center_x; +position_new(2,:) = position_new(2,:)+center_y; + +position_new_x = reshape(position_new(1,:),size_y,size_x); +position_new_y = reshape(position_new(2,:),size_y,size_x); + +[J,mask]= m_interp4(I,position_new_x,position_new_y); + +JJ = J(center(2)-window_size_h(2):center(2)+window_size_h(2),... + center(1)-window_size_h(1):center(1)+window_size_h(1)); +mask = mask(center(2)-window_size_h(2):center(2)+window_size_h(2),... + center(1)-window_size_h(1):center(1)+window_size_h(1)); + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist.m new file mode 100755 index 0000000..f8dbd32 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist.m @@ -0,0 +1,45 @@ +function A = apply_image(gx,gy,I,wc) +% +% aout = apply_image(gx,gy,wc) +% +% + +[nr,nc] =size(gx); + +w = 2*wc+1; + +s1 = floor(nr/w); +s2 = floor(nc/w); + +A = zeros(s1*s2,s1*s2); + +yid = 0; +for j=wc+1:w:nr-wc-1, + yid = yid+1; + xid = 0; + for k=wc+1:w:nc-wc-1, + xid = xid + 1; + c1 = [k,j]; + + yyid = 0; + fprintf('.'); + for jj=wc+1:w:nr-wc-1, + yyid = yyid+1; + xxid = 0; + for kk=wc+1:w:nc-wc-1, + xxid = xxid + 1; + + c2 = [kk,jj]; + + a = compute_diff_patch(get_win(gx,c1,[wc,wc]),... + get_win(gy,c1,[wc,wc]),... + get_win(gx,c2,[wc,wc]),... + get_win(gy,c2,[wc,wc]),... + get_win(I,c1,[wc,wc]),... + get_win(I,c2,[wc,wc])); + + A(yid+(xid-1)*s1,yyid+(xxid-1)*s1) = a; + end + end + end +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist2.m new file mode 100755 index 0000000..dc31469 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist2.m @@ -0,0 +1,46 @@ +function A = apply_image(gx,gy,I,wc) +% +% aout = apply_image(gx,gy,wc) +% +% + +[nr,nc] =size(gx); + +w = 2*wc+1; + +lws = 4; + +s1 = floor(nr/w); +s2 = floor(nc/w); + +A = zeros(s1*s2,s1*s2); + +for j=1:s1, + for k=1:s2, + c1 = [(wc+1)+(k-1)*w,(wc+1)+(j-1)*w]; + fprintf('.'); + + for jj=j-lws:j+lws, + for kk=k-lws:k+lws, + + c2 = [(wc+1)+(kk-1)*w,(wc+1)+(jj-1)*w]; + if ( (jj>0) & (kk>0) & (jj <= s1) & (kk <= s2)) + a = compute_diff_patch(get_win(gx,c1,[wc,wc]),... + get_win(gy,c1,[wc,wc]),... + get_win(gx,c2,[wc,wc]),... + get_win(gy,c2,[wc,wc]),... + get_win(I,c1,[wc,wc]),... + get_win(I,c2,[wc,wc])); + + dx = abs(k-kk);dy = abs(j-jj); + sp_diff = exp(-sqrt(dx.^2+dy.^2)/4); + A(j+(k-1)*s1,jj+(kk-1)*s1) = a*sp_diff; + + end + + end + end + end +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text.m new file mode 100755 index 0000000..5cdb201 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text.m @@ -0,0 +1,70 @@ +function A = apply_image(I,bars,wc) +% +% aout = apply_image(gx,gy,wc) +% +% + +[nr,nc] =size(I); + +w = 2*wc+1; + +lws = 4; + +gap = 10; + +s1 = floor((nr-w)/gap); +s2 = floor((nc-w)/gap); + +A = zeros(s1*s2,s1*s2); + +sigma.text = 0.20; +sigma.mag = 0.20; +sigma.inten = 0.15; + +for j=1:s1, + for k=1:s2, + + + c1 = [(wc+1)+(k-1)*gap,(wc+1)+(j-1)*gap]; + fprintf('.'); + for jj=j-lws:j+lws, + for kk=k-lws:k+lws, + + c2 = [(wc+1)+(kk-1)*gap,(wc+1)+(jj-1)*gap]; + if ( (jj>0) & (kk>0) & (jj <= s1) & (kk <= s2)), + + J1 = get_win(I,c1,[wc,wc]); + J2 = get_win(I,c2,[wc,wc]); + + Jbars1 = get_win5(bars,c1,[wc,wc]); + Jbars2 = get_win5(bars,c2,[wc,wc]); + + + hists1 = get_hist(J1,Jbars1); + hists2 = get_hist(J2,Jbars2); + + cumhists1 = get_cumhist(hists1); + cumhists2 = get_cumhist(hists2); + + + diff.inten = max(abs(cumhists1.inten-cumhists2.inten)); + diff.mag = max(abs(cumhists1.mag-cumhists2.mag)); + diff.text = max(max(abs(cumhists1.text-cumhists2.text))); + + diffs = max([diff.inten/sigma.inten,... + diff.mag/sigma.mag,... + diff.text/sigma.text]); + + dx = abs(k-kk);dy = abs(j-jj); + sp_diff = sqrt(dx.^2+dy.^2)/4; + + A(j+(k-1)*s1,jj+(kk-1)*s1) = exp(-diffs-sp_diff); + + end + + end + end + end +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text2.m new file mode 100755 index 0000000..5c6e0d4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text2.m @@ -0,0 +1,58 @@ +function A = apply_image(I,bars,wc) +% +% aout = apply_image(gx,gy,wc) +% +% + +[nr,nc] =size(I); + +w = 2*wc+1; + +lws = 4; + +gap = 10; + +mag = sum(bars,3); + +s1 = floor((nr-w)/gap) +s2 = floor((nc-w)/gap) + +A = zeros(s1*s2,s1*s2); + +sigma.text = 0.20; +sigma.mag = 0.20; +sigma.inten = 0.15; + +for j=1:s1, + for k=1:s2, + + + c1 = [(wc+1)+(k-1)*gap,(wc+1)+(j-1)*gap]; + fprintf('.'); + for jj=j-lws:j+lws, + for kk=k-lws:k+lws, + + c2 = [(wc+1)+(kk-1)*gap,(wc+1)+(jj-1)*gap]; + if ( (jj>0) & (kk>0) & (jj <= s1) & (kk <= s2)), + + ces = [c1,c2]-wc; + cum = compute_dist_hist(I,mag,bars,[ces,w]); + + diffs = max([cum(1)/sigma.inten,... + cum(2)/sigma.mag,... + cum(3)/sigma.text]); + + dx = abs(k-kk);dy = abs(j-jj); + sp_diff = sqrt(dx.^2+dy.^2)/4; + + A(j+(k-1)*s1,jj+(kk-1)*s1) = exp(-diffs-sp_diff); + + end + + end + end + + end +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text3.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text3.m new file mode 100755 index 0000000..a7ff408 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text3.m @@ -0,0 +1,84 @@ +function [A,D] = pair_dist_text3(Cum,cumhists) +% +% A = pair_dist_text3(Cum,cumhists); +% +% + +s1 = Cum(2); +s2 = Cum(1); + +st = Cum(3) + Cum(4) + 1; +ed = size(cumhists,1); + +cumhists = cumhists(st:ed,:); + +np = size(cumhists,2); + +sigma.text = 0.20; +sigma.mag = 0.20; +sigma.inten = 0.15; + +lws = 4; + + +k = sqrt(2)/2; +M = 8*6; +N = k*sqrt(M); + +r1 = 0.001; +r2 = 0.001; + +c = N/(1+ (sqrt(1-0.5*(r1*r1+r2*r2)))*(0.25-0.75/N)); + +D = zeros(1,s1*s2); + +nn = 1; +for j =1:s1, + for k=1:s2, + + id = j*s2+k; + + cum_filter1 = reshape(cumhists(:,id),8,6)'; + [na1,nb1,nc1,nd1] = ks_2d(cum_filter1); + + + fprintf('.'); + for jj=j-lws:j+lws, + for kk=k-lws:k+lws, + + if ( (jj>0) & (kk>0) & (jj <= s1) & (kk <= s2)), + + idn = jj*s2+k; + + cum_filter2 = reshape(cumhists(:,idn),8,6)'; + [na2,nb2,nc2,nd2] = ks_2d(cum_filter2); + + + diffa = abs(na2-na1);diffb =abs(nb2-nb1); + diffc = abs(nc2-nc1);diffd = abs(nd2-nd1); + maxs(1) = max(max(diffa));maxs(2) = max(max(diffb)); + maxs(3) = max(max(diffc));maxs(4) = max(max(diffd)); + + + maxs = maxs/6; + + d = min(1,signif(c*max(maxs))); + + ids(nn) = id; + idns(nn) = idn; + B(nn) = d; + + D(id) = D(id) + d; + D(idn) = D(idn) + d; + + nn = nn+1; + + end + + end + end + + end +end + +A = sparse(ids,idns,b); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text4.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text4.m new file mode 100755 index 0000000..83f3d49 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pair_dist_text4.m @@ -0,0 +1,81 @@ +function [A] = pair_dist_text4(Iw,Cresult,cso) +% +% A = pair_dist_text3(Cum,cumhists); +% +% + +%s1 = Cum(2);s2 = Cum(1); + +figure(1);hold on;plot(cso(1),cso(2),'bo'); + +ws = [10,10]; + +lws = 3; +gap = 7; + +J1 = get_win(Iw,round(cso),ws); +Jbar1 = abs(get_win5(Cresult,round(cso),ws)); +hists1 = get_hist(J1,Jbar1); +cumhists1 = get_cumhist(hists1); +[na1,nb1,nc1,nd1] = ks_2d(cumhists1.text'); + +figure(4);colormap(gray); +imagesc(reshape(abs(Jbar1),size(Jbar1,1),size(Jbar1,2)*size(Jbar1,3))); +colorbar;axis('image');drawnow; + +figure(2); +subplot(2,5,1);imagesc(J1);%colormap(gray) +subplot(2,5,2);imagesc(hists1.text');title('hist_1');colorbar; +subplot(2,5,3);imagesc(cumhists1.text');title('cumhist_1');colorbar; +subplot(2,5,4);imagesc(nc1);title('nc_1');colorbar +subplot(2,5,5);imagesc(nb1);title('nb_1');colorbar +drawnow; + +k = sqrt(2)/2;M = prod(size(cumhists1.text)); +N = k*sqrt(M); +r1 = 0.001;r2 = 0.001; +con = N/(1+ (sqrt(1-0.5*(r1*r1+r2*r2)))*(0.25-0.75/N)); + +jid = 1; +for jj=cso(2)-lws*gap:gap:cso(2)+lws*gap, + kid = 1; + for kk=cso(1)-lws*gap:gap:cso(1)+lws*gap, + + figure(1);hold on; plot(kk,jj,'r*');drawnow; + + J2 = get_win(Iw,round([kk,jj]),ws); + Jbar2 = get_win5(Cresult,round([kk,jj]),ws); + + hists2 = get_hist(J2,Jbar2); + cumhists2 = get_cumhist(hists2); + [na2,nb2,nc2,nd2] = ks_2d(cumhists2.text'); + + figure(2); + subplot(2,5,6);imagesc(J2);%colormap(gray) + subplot(2,5,7);imagesc(hists2.text');title('hist_2');colorbar + subplot(2,5,8);imagesc(cumhists2.text');title('cumhist_2');colorbar + + diffa = abs(na2-na1);diffb =abs(nb2-nb1); + diffc = abs(nc2-nc1);diffd = abs(nd2-nd1); + + maxs(1) = max(max(diffa));maxs(2) = max(max(diffb)); + maxs(3) = max(max(diffc));maxs(4) = max(max(diffd)); + + maxs = maxs/6;for j=1:4, sig(j) = signif(con*maxs(j)); end + + subplot(2,5,9);imagesc(diffc);title('diff_{nc}');colorbar + subplot(2,5,10);imagesc(diffb);title('diff_{nb} ');colorbar + + disp(sprintf('max diff is %f\n',min(sig))); + + A(jid,kid) = min(1,min(sig)); + + %disp('press return to continue'); + pause(3); + + kid = kid+1; + end + jid = jid+1; +end + +figure(1);hold off; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/patch_cat.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/patch_cat.m new file mode 100755 index 0000000..0bf22c3 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/patch_cat.m @@ -0,0 +1,9 @@ +function T = patch_cat(dotfilter,text_des) + +T(:,:,2:7,:) = text_des; +clear text_des; + +for k=1:size(T,4), + T(:,:,1,k) = dotfilter(:,:,1,k); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pgmread.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pgmread.m new file mode 100755 index 0000000..620408a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/pgmread.m @@ -0,0 +1,15 @@ +function img = pgmread(filename,size) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + +fid = fopen(filename,'r'); + +for j=1:4, + a = fgetl(fid); +end + +img = fscanf(fid,'%d',size); +img = img'; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/poisson.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/poisson.m new file mode 100755 index 0000000..99d49b6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/poisson.m @@ -0,0 +1,44 @@ +function [x] = Poisson(lambda); +%Poisson generates a random variable with a Poisson +% distribution +% Pr(x = n) = lambda^n exp(-lambda)/n +% +% [x] = Poisson(lambda); +% lambda: the parameter for the Poisson distribution +% (default is 1) +% x : the output number +% + +%% +%% (C) Thomas K. Leung +%% University of California at Berkeley +%% Apr 25, 1995. +%% + +%%% Notice that in this implementation, we are comparing log(Z) with +%%% T. In fact, we can compare T = exp(-lambda) with Z, which will +%%% save us from computing a large number of log's. However, when +%%% lambda is bigger than 709, exp(-lambda) = 0, which causes problem. + +if nargin == 0 + lambda = 1; +end + +if lambda < 0 + lambda = 1; +end + +P = 0; +N = 0; +T = -lambda; + +rand('seed',sum(100*clock)); + +while P >= T + Z = rand(1); + P = P + log(Z); + N = N + 1; +end + +x = N; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/poissonfield.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/poissonfield.m new file mode 100755 index 0000000..8051867 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/poissonfield.m @@ -0,0 +1,53 @@ +function [x,y,success] = PoissonField(int,sx,sy,ir); +%BF_HardCore Generates a hard core Poisson field +% [x,y] = Poisson_HC(int,ir,sx,sy); +% int: intensity of the field. (default is 1) +% ir : inhibition radius (default is 1) +% sx : size in x (default 100) +% sy : size in y (default 100) +% x : x coordinates +% y : y coordinates +% + +%% +%% (C) Thomas K. Leung +%% University of California at Berkeley +%% April 26, 1995. +%% leungt@cajal.cs.berkeley.edu +%% + +%% +%% Generate each point in sequence and reject it if it's too close to +%% previous ones. +%% + +if nargin <= 0 + int = 1; + sx = 100; + sy = 100; + ir = 0; +elseif nargin <= 1 + sx = 100; + sy = 100; + ir = 0; +elseif nargin <= 2 + sy = 100; + ir = 0; +elseif nargin <= 3 + ir = 0; +end + +%% +%% Notice that the average number of a poisson process is the +%% parameter lambda. This is consistent with our definition of the +%% intensity here. Intensity is the mean number of points inside each +%% unit area. Therefore, in a window of sx by sy, we should get +%% int*sx*sy number of points on average which is the mean number of +%% arrivals in a Poisson Process +%% + +[n] = poisson(int * sx * sy); + +[x,y,success] = binomialfield(n,sx,sy,ir); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back.m new file mode 100755 index 0000000..069fe61 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back.m @@ -0,0 +1,24 @@ +function mask = proj_back(I,hw,mask_s) +% +% mask = proj_back(I,hw,mask_s) +% +% + + +[nr,nc] = size(I2); + +hw = 3; +st_sz = 2*hw + 1; + +nr_chank = floor(nr/st_sz); +nc_chank = floor(nc/st_sz); + +[x,y] = meshgrid(1:nc,1:nr); + +ct_chank_x = round((x-hw-1)/st_sz) + 1; +ct_chank_y = round((y-hw-1)/st_sz) + 1; + +idx = (ct_chank_x - 1)*nr_chank + ct_chank_y; + +mask = full(sparse(y,x,mask_s(idx(:)))); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back_id.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back_id.m new file mode 100755 index 0000000..9db322e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/proj_back_id.m @@ -0,0 +1,19 @@ +function [vbig,bnr,bnc] = proj_back_id(v,gcs,gce,grs,gre) +% +% vbig = proj_back_id(v,gcs,gce,grs,gre) +% + +nr = max(gre)+1; +nc = max(gce)+1; + +sw = 3; +gap = 2*sw+1; + +bnc = nc*gap; +bnr = nr*gap; + +[x,y] = meshgrid(1:bnc,1:bnr); + +idx = grs(y(:))+1+gcs(x(:))*nr; + +vbig = full(sparse(y(:),x(:),v(idx))); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/quant.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/quant.m new file mode 100755 index 0000000..c2dfae6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/quant.m @@ -0,0 +1,20 @@ +function [x,map] = quant(d1,d2,d3,nbin,ws) + +if (~exist('ws')), + ws = [1,1,1]; +end + +d1 = d1-min(d1); +d2 = d2-min(d2); +d3 = d3-min(d3); + +d1 = d1/max(d1); +d2 = d2/max(d2); +d3 = d3/max(d3); + +d1 = d1*ws(1); +d2 = d2*ws(2); +d3 = d3*ws(3); + + +[x,map] = vmquant(d1,d2,d3,nbin); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpdm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpdm.m new file mode 100755 index 0000000..9a1068e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpdm.m @@ -0,0 +1,8 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); +I = fscanf(fid,'%d',[A(1),A(2)]); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm.m new file mode 100755 index 0000000..48ecd78 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm.m @@ -0,0 +1,10 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); +I = fscanf(fid,'%f',[A(1),A(2)]); + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_id.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_id.m new file mode 100755 index 0000000..5f5c4f7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_id.m @@ -0,0 +1,21 @@ +function Is = readpfm(filename,ids,nodes) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); + +Is = zeros(length(ids),nodes); + +ix = 1; +for j=1:max(ids), + I = fscanf(fid,'%f',nodes); + if (find(ids==j)), + Is(ix,:) = I(:)'; + ix = ix+1; + fprintf('.'); + end +end + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_idf.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_idf.m new file mode 100755 index 0000000..d7916e7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpfm_idf.m @@ -0,0 +1,29 @@ +function Is = readpfm(filename,ids,nodes) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); + +Is = zeros(length(ids),nodes); + +idt = sort(ids); + +idh = 1; + +ix = 1; +for j=1:length(ids), + + gap = idt(j) - idh; + fprintf('%d',gap); + + I = fscanf(fid,'%f',nodes*gap); + I = fscanf(fid,'%f',nodes); + + Is(find(ids==idt(j)),:) = I(:)'; + fprintf('.'); + + idh = idt(j)+1; +end + + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpgm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpgm.m new file mode 100755 index 0000000..a5fd7f2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpgm.m @@ -0,0 +1,26 @@ +function img = pgmread(filename) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + + +fid = fopen(filename,'r'); +fscanf(fid, 'P5\n'); +cmt = '#'; +while findstr(cmt, '#'), + cmt = fgets(fid); + if length(findstr(cmt, '#')) ~= 1, + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end +end + +fgets(fid); + +%img = fscanf(fid,'%d',size); +%img = img'; + +img = fread(fid,[y,x],'uint8'); +img = img'; +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpnm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpnm.m new file mode 100755 index 0000000..ab78c2c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readpnm.m @@ -0,0 +1,21 @@ +function I = readpnm(name) + + fid = fopen(name, 'r'); + fscanf(fid, 'P5\n'); + cmt = '#'; + while findstr(cmt, '#') == 1 + cmt = fgets(fid); + if findstr(cmt, '#') ~= 1 + YX = sscanf(cmt, '%d %d %d'); + y = YX(1); x = YX(2); nb = YX(3); + end + end + fgets(fid); + packed = fscanf(fid,'%f',[nb*y*x]); + + for j = 1:nb, + I(:,:,j) = reshape(packed(j:nb:nb*y*x),y,x)'; + end + + fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readppm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readppm.m new file mode 100755 index 0000000..300f597 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/readppm.m @@ -0,0 +1,19 @@ +function [r, g, b] = readppm(name) + + fid = fopen(name, 'r'); + fscanf(fid, 'P6\n'); + cmt = '#'; + while findstr(cmt, '#') == 1 + cmt = fgets(fid); + if findstr(cmt, '#') ~= 1 + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end + end + fgets(fid); + packed = fread(fid,[3*y,x],'uint8')'; + r = packed(:,1:3:3*y); + g = packed(:,2:3:3*y); + b = packed(:,3:3:3*y); + fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/record.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/record.m new file mode 100755 index 0000000..9626038 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/record.m @@ -0,0 +1,6 @@ +load patch1; +doog2 = mkdoog2(2,10,0,80); +dog2 = rotate_J(90,doog2); + +H = mkf_test(dog2,size(patch1),-1,0.00001,2,-1); +o = BfilterS(patch1,H,size(dog));figure(4);colormap(gray);imagesc(o.*(o>0)); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/recursive_cut_tc.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/recursive_cut_tc.m new file mode 100755 index 0000000..a8c9362 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/recursive_cut_tc.m @@ -0,0 +1,140 @@ +%function [groups,ids] = recursive_cut(ncutv,fn_base) +% +% +% function [groups,ids] = recursive_cut(ncutv,threshold,spthresh) +% +% + +ncutv= ncutv_o(:,1:4); + +fn_base = fn; +%fn_base = '130040'; + +nvv = size(ncutv_o,2); +nbin = 24; + +ids = []; +groups = []; +labels = []; + +load cmaps +cmap = cmapg; + +j = 1; +done = 0; +np = size(ncutv,1); +nv = size(ncutv,2); + +%%%%%% find the cut for the first ncut vector +ev_id = 2; +para = [nvv ev_id nr nc 100]; +Gmask = ones(nr,nc); +%threshold = find_cutpoint(ncutv(:,ev_id),cmapg,nbin);threshold = threshold(1:end-1); +threshold = linspace(min(ncutv(:,ev_id)),max(ncutv(:,ev_id)),nbin); +[cut_threshold,max_asso] = find_bst_cut(fn_base,para,threshold,Gmask); +disp(max_asso); + +id1 = find(ncutv(:,ev_id)<=cut_threshold); +id2 = find(ncutv(:,ev_id)>cut_threshold); + +groups = [groups,id1(:)']; +ids = [ids,length(id1)]; + +groups = [groups,id2(:)']; +ids = [ids,length(groups)]; + + +for j=3:nv, + fprintf('j = %d\n',j); + % expand the current level, + new_groups = []; + new_ids = []; + + + figure(4);ims(ncutv(:,j),nr,nc);title(num2str(j)); + + figure(1);clf + disp_groups(groups,ids,nr,nc); + drawnow; + + %figure(3); + % for each leaves, + mx = max(ncutv(:,j))-min(ncutv(:,j)); + %mx = std(ncutv(:,j)); + + base_id =1; + for k=1:length(ids), + old_groups = groups(base_id:ids(k)); + + v = ncutv(old_groups,j); + change_v = max(v)-min(v); + %change_v = std(v); + n1 = sum(v>(min(v)+0.85*change_v));%n1 = n1/length(old_groups); + n2 = sum(v<=(min(v)+0.15*change_v));%n2 = n2/length(old_groups); + disp(sprintf('n1 = %f, n2 = %f',n1,n2)); + + figure(2); + Gmask = zeros(np,1); + Gmask(old_groups) = Gmask(old_groups)+1; + drawnow; + ims(ncutv(:,j).*Gmask,nr,nc); + + disp(sprintf('!!!!!!!!!!!!!RATIO= %f',change_v/mx)) + + %pause; + + if (((change_v/mx)>0.5) & (n1>10) &(n2>10)), + + ev_id = j; + + %threshold = find_cutpoint(ncutv(old_groups,ev_id),cmapg,nbin);threshold = threshold(1:end-1); + threshold = linspace(min(ncutv(:,ev_id)),max(ncutv(:,ev_id)),nbin); + para = [nvv ev_id nr nc 100]; + [cut_threshold,max_asso] = find_bst_cut(fn_base,para,threshold,Gmask); + + disp(max_asso); + + if (max_asso>1.2), + id1 = find(ncutv(old_groups,ev_id)<=cut_threshold); + id2 = find(ncutv(old_groups,ev_id)>cut_threshold); + + figure(5); + subplot(1,2,1);maskt= zeros(np,1);maskt(old_groups(id1))=1+maskt(old_groups(id1));ims(maskt,nr,nc); + subplot(1,2,2);maskt= zeros(np,1);maskt(old_groups(id2))=1+maskt(old_groups(id2));ims(maskt,nr,nc); + + new_groups = [new_groups,old_groups(id1)]; + new_ids = [new_ids,length(new_groups)]; + + + new_groups = [new_groups,old_groups(id2)]; + new_ids = [new_ids,length(new_groups)]; + else + fprintf(' keep '); + new_groups = [new_groups,old_groups]; + new_ids = [new_ids,length(new_groups)]; + end + + else + fprintf(' keep '); + new_groups = [new_groups,old_groups]; + new_ids = [new_ids,length(new_groups)]; + end + fprintf('\n'); + base_id = ids(k) + 1; + end + + + + groups = new_groups; + ids = new_ids; + + figure(1);disp_groups(groups,ids,nr,nc); + + fprintf('press return\n'); + pause; + j= j+1; +end + +fprintf('total group = %d \n',length(ids)); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/reduce_all.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/reduce_all.m new file mode 100755 index 0000000..d7d31f8 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/reduce_all.m @@ -0,0 +1,8 @@ +function b = reduce_all(a) + +numband = size(a,3); + +for j=1:numband, + + b(:,:,j) = reduce(a(:,:,j)); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/rotate_J.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/rotate_J.m new file mode 100755 index 0000000..12f29b6 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/rotate_J.m @@ -0,0 +1,30 @@ +function J = compute_J(angle,I) +%% function J = compute_J(angle,I) +% + +[size_y,size_x] = size(I); + +[center_x,center_y] = find_center(size_x,size_y); + +a = angle * pi/180; +A = [cos(a),-sin(a);sin(a),cos(a)]; + +[XX,YY] = meshgrid(1:size_x,1:size_y); + +x = reshape(XX,size_x*size_y,1); +y = reshape(YY,size_x*size_y,1); +index(:,1) = x-center_x; +index(:,2) = y-center_y; + +position_new = A*index'; +position_new(1,:) = position_new(1,:)+center_x; +position_new(2,:) = position_new(2,:)+center_y; + +position_new_x = reshape(position_new(1,:),size_y,size_x); +position_new_y = reshape(position_new(2,:),size_y,size_x); + +J = m_interp4(I,position_new_x,position_new_y); + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/session.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/session.m new file mode 100755 index 0000000..70fabbf --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/session.m @@ -0,0 +1,4 @@ +t = rj(1:50,19:50); tt = interp4(t,1); +sgn = mksgn2(182,2,[91,9]); +H = mkf_test(sgn,size(tt),1,0.01,2,300); +o = BfilterS(tt,H,size(sgn));figure(1);imagesc(o.*(o>0));axis('equal'); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_cumhist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_cumhist.m new file mode 100755 index 0000000..fe82e64 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_cumhist.m @@ -0,0 +1,28 @@ +function show_hist(cumhists,bins,fig_id,hold_flag,ct) +%% +% show_hist(cumhists,bins,fig_id,ct) +% +% + + if (~exist('ct')), + ct = 'b-o'; + end + + figure(fig_id); + + subplot(3,3,1);plot(bins.inten,cumhists.inten,ct); + + if (hold_flag == 1), hold on;else hold off; end + + for j=1:size(cumhists.text,2), + subplot(3,3,1+j); + plot(bins.text,cumhists.text(:,j),ct); + if (hold_flag == 1), hold on;else hold off; end + end + + subplot(3,3,8); + plot(bins.mag,cumhists.mag,ct); + if (hold_flag == 1), hold on;else hold off; end + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_hist.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_hist.m new file mode 100755 index 0000000..efaf899 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/show_hist.m @@ -0,0 +1,23 @@ +function show_hist(hists,bins,fig_id) +%% +% show_hist(hists,bins,fig_id) +% +% + + figure(fig_id); + + subplot(3,3,1);bar(bins.inten,hists.inten); + + %maxval = max(max(max(abs(Jbar)))); + + for j=1:size(hists.text,2), + subplot(3,3,1+j);% hist(reshape(abs(Jbar(:,:,j)),prod(w),1),[1:10:maxval+1]); + bar(bins.text,hists.text(:,j)); + end + + subplot(3,3,8);%hist(reshape(sum(abs(Jbar),3),prod(w),1),[1:10:161]); + bar(bins.mag,hists.mag); + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm.m new file mode 100755 index 0000000..1833062 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm.m @@ -0,0 +1,45 @@ +function [T,A,M2,TAM]=showsmm(L1,L2,phi,maxM); +% [T,A,M]=showsmm(L1,L2,phi,maxM); + +if (~exist('maxM')), +% needs to know upper bound on M for given window function in smm +maxM=0.18; % temporary +end + + +A=1-L2./(L1+eps); +T=2*(phi+pi/2)/(2*pi); +M=L1+L2; +M2=min(M/maxM,1); % keep it from exceeding 1 +%M2 = sigmoid(M,maxM,30); + +matlab5on = 1; + +if matlab5on == 1, + TAM=hsv2rgb(T,A,M2); + + figure(3); + image(TAM); + axis('tightequal'); +else + H = [reshape(T,prod(size(T)),1),... + reshape(A,prod(size(A)),1),... + reshape(M2,prod(size(M2)),1)]; + M = hsv2rgb(H); + [Ic,map] =vmquant(M(:,1),M(:,2),M(:,3),256); + + image(reshape(Ic,size(T,1),size(T,2)));colormap(map); +end + +if 0 + plot3(A(:).*M(:).*cos(2*pi*T(:)),A(:).*M(:).*sin(2*pi*T(:)),M(:),'.','markersize',15) + axis([-1 1 -1 1 0 1]) + [x,y,z] = cylinder(ones(1,5)); + x=x.*z; + y=y.*z; + hold on + h=mesh(x,y,z); + set(h,'edgecolor',[.2 .2 .2]); + hidden off + hold off +end \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm_v5.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm_v5.m new file mode 100755 index 0000000..937303d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/showsmm_v5.m @@ -0,0 +1,34 @@ +function [T,A,M2,TAM]=showsmm(L1,L2,phi,maxM); +% [T,A,M]=showsmm(L1,L2,phi,maxM); + +if (~exist('maxM')), +% needs to know upper bound on M for given window function in smm +maxM=0.18; % temporary +end + + +A=1-L2./(L1+eps); +T=2*(phi+pi/2)/(2*pi); +M=L1+L2; +M2=min(M/maxM,1); % keep it from exceeding 1 +%M2 = sigmoid(M,maxM,30); + + TAM=hsv2rgb(T,A,M2); + + figure(3); + image(TAM); + axis('tightequal'); + + +if 0 + plot3(A(:).*M(:).*cos(2*pi*T(:)),A(:).*M(:).*sin(2*pi*T(:)),M(:),'.','markersize',15) + axis([-1 1 -1 1 0 1]) + [x,y,z] = cylinder(ones(1,5)); + x=x.*z; + y=y.*z; + hold on + h=mesh(x,y,z); + set(h,'edgecolor',[.2 .2 .2]); + hidden off + hold off +end \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/sigmoid.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/sigmoid.m new file mode 100755 index 0000000..996f7fe --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/sigmoid.m @@ -0,0 +1,10 @@ +function a = sigmoid(x,offset,sig) +% +% a = sigmoid(x,offset,sig) +% +% a = ones(size(x))./(1+exp(-(x-offset)/sig)); +% + + +a = ones(size(x))./(1+exp(-(x-offset)/sig)); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif.m new file mode 100755 index 0000000..f49eefc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif.m @@ -0,0 +1,22 @@ +function a = signif(b) + +js = [1:101]; + + +if 0, +d = (-ones(size(js))).^(js-1); +d1 = exp(-2*(js.*js)*b*b); + +a = 2*sum(d.*d1); + +end + +d1 = exp(-2*(js.*js)*b*b); +d2 = 4*(js.*js)*b*b - 1; + +a = 2*sum(d1.*d2); + +if (b<0.03),a = 1;end + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif_N.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif_N.m new file mode 100755 index 0000000..82dc914 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/signif_N.m @@ -0,0 +1,10 @@ +function a = signif_N(b,N) +% +% +% + +Ne = sqrt(N*0.5); + +cof = Ne + 0.155 + 0.24/Ne; + +a= signif(cof*b); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/smooth.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/smooth.m new file mode 100755 index 0000000..919b53a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/smooth.m @@ -0,0 +1,20 @@ +% smooth an image +% coordinates (r, c) follow matrix convention; +% the gaussian is truncated at x = +- tail, and there are samples samples +% inbetween, where samples = hsamples * 2 + 1 + +function g = smooth(image, hsamples) + +tail=4; +samples = hsamples * 2 + 1; + +x = linspace(-tail, tail, samples); +gauss = exp(-x.^2); +n = gauss * ones(samples,1); +gauss = gauss/n; + + +g = conv2(conv2(image, gauss), gauss'); + +g = conv_trim(g, hsamples, hsamples); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/startup.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/startup.m new file mode 100755 index 0000000..8d38803 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/startup.m @@ -0,0 +1,9 @@ +%path(path,'/usr/sww/matlab-4.2c/toolbox/images'); +home_dir = '/home/barad-dur/vision/malik/jshi/'; +path(path,[home_dir,'matlab/toolbox/io']) +path(path,[home_dir,'matlab/pyramid']); +path(path,[home_dir,'matlab/toolbox/filter']) +path(path,[home_dir,'matlab/toolbox/disp']) +path(path,[home_dir,'matlab/vision/vision94/tracking/']) +path(path,[home_dir,'proj/grouping/laso']); +path(path,[home_dir,'proj/grouping/eig']); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarp.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarp.m new file mode 100755 index 0000000..60a4530 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarp.m @@ -0,0 +1,9 @@ +function J = swarp(I) + +[nr,nc] = size(I); + +center_x = round(0.5*nc); +center_y = round(0.5*nr); + +J = [I(center_y:nr,center_x:nc),I(center_y:nr,1:center_x-1);... + I(1:center_y-1,center_x:nc),I(1:center_y-1,1:center_x-1)]; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarpback.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarpback.m new file mode 100755 index 0000000..513bc25 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/swarpback.m @@ -0,0 +1,12 @@ +function J = swarpback(I); + +[nr,nc] = size(I); + +center_x = round(0.5*nc); +center_y = round(0.5*nr); + +cx= center_x -1; +cy= center_y -1; + +J = [I(nr-cy+1:nr,nc-cx+1:nc),I(nr-cy+1:nr,1:(nc-center_x+1));... + I(1:(nr-center_y+1),nc-cx+1:nc),I(1:(nr-center_y+1),1:(nc-center_x+1))]; \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test.m new file mode 100755 index 0000000..12470eb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test.m @@ -0,0 +1,110 @@ + +fn = '130065'; nr = 30;nc = 49; +%nr = 49;nc = 30; +%nc = 68;nr = 43; +%nr =49;nc = 30; + +basedir = 'plaatje_data/newdata/'; +if 1, +fname = sprintf('%s%s_eigvec.pfm',basedir,fn); +eigv = readpfm(fname); +fname = sprintf('%s%s_eigval.pfm',basedir,fn); +eigval = readpfm(fname); + +fname = sprintf('%s%s_ncutvec.pfm',basedir,fn); +ncutv = readpfm(fname); +fname = sprintf('%s%s_ncutval.pfm',basedir,fn); +ncutval = readpfm(fname); +else +fname = sprintf('%sncutvec_%s.pfm',basedir,fn); +ncutv = readpfm(fname); +fname = sprintf('%sncutval_%s.pfm',basedir,fn); +ncutval = readpfm(fname); +end + + +fname = sprintf('images/%s.pgm',fn); +I = readpgm(fname); +cutsz = 20; I = cutoff(I,cutsz); +figure(3);im(I);colormap(gray); + +figure(6); +for j=1:min(8,size(ncutv,2)-1), + subplot(3,3,j); + im(reshape(ncutv(:,j+1),nr,nc));colorbar + title(num2str(ncutval(j+1,1))); +end +%cm = sprintf('print -dps ncut_%s',fn);disp(cm);eval(cm); +subplot(3,3,9);im(I);axis('off'); + +ev = eigval(:,1); +figure(5);hold off;clf;subplot(1,2,1); +%semilogy((ev(1:end-1) - ev(2:end))./ev(1:end-1),'x-');grid on; +plot((ev(1:end-1) - ev(2:end))./ev(1:end-1),'x-');grid on; +%semilogy(0.01*ones(size(ev(2:end-1))),'r-');semilogy(0.005*ones(size(ev(2:end-1))),'r-');semilogy(0.0025*ones(size(ev(2:end-1))),'r-');grid on;hold off; +subplot(1,2,2); +%semilogy(ev(1:end-1)-ev(2:end),'p-');grid on; +semilogy((ev(1:end-1) - ev(2:end))/ev(1),'x-');grid on; + + +ncutv_o = ncutv; + +recursive_cut_tc; + +%[groups,ids] = recursive_cut(ncutv(:,1:4),fn); + +masks = make_masks(groups,ids,nr,nc); + +cm = sprintf('save masks_%s masks ncutv_o groups ids nr nc',fn); +disp(cm); + +eval(cm); + + +%%%%%%%%%%%%%%%%%% +fn = '130040'; +cm = sprintf('load masks_%s',fn); +disp(cm); +eval(cm); + +fn = '130040'; +fname= sprintf('images/%s.pgm',fn); +I = readpgm(fname);cutsz = 20; I = cutoff(I,cutsz); +figure(3);im(I);colormap(gray); +hw = 2; %nr = 43;nc=68; +gap = 2*hw+1; +%nr = 30;nc=49; +Is = I(1:nr*gap,1:nc*gap); +figure(3);im(Is);axis('off'); + +%cm = sprintf('print -deps I_%s',fn);disp(cm);eval(cm); + + + +%masks = make_masks(groups,ids,nr,nc); +figure(2);disp_groups(groups,ids,nr,nc); + +figure(1); +Imasks = disp_Imask(Is,nr,nc,hw,masks); + +for j=1:length(ids), + figure(4);colormap(gray);clf + im(Imasks(:,:,j));axis('off'); + cm = sprintf('print -deps result_cut_%s_%d',fn,j); + disp(cm);eval(cm); + + %print -deps result_cut_134011_1 +end + + +if 0, + +%load st_134013 + +fn = '134013_t'; + +I_max = 250; +tex_max = 40; + +writeout_feature(I1,T1,fn,I_max,tex_max); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test1.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test1.m new file mode 100755 index 0000000..691b63e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test1.m @@ -0,0 +1,175 @@ + +fnameI = '130068'; + +cm = sprintf('load filter_%s.pgm.mat',fnameI); +disp(cm); +eval(cm); + +text_des = abs(text_des); + + + %%%% cutoff margins, +margin = 6+10; + +Iw = cutoff(I,margin); + +T1= reshape(text_des,size(text_des,1),size(text_des,2),size(text_des,3)*size(text_des,4)); +T1 = cutoff(T1,margin); + +%%%%% reduce resolution + + + +T1 = reduce_all(T1); +T1 = reduce_all(T1); + +im5(T1,5,6); + +cm = sprintf('writepnm5(''%s_f.pnm'',%s)',fnameI,'T1/70'); + +% disp(cm);eval(cm); + +nr = size(T1,1); +nc = size(T1,2); + +% D = mreadpfm('D_134011_f.pnm.pfm'); + +% figure(3);imagesc(reshape(D,nc,nr)');axis('image');colorbar + +if 0, +figure(7); +subplot(3,1,1);hist(reshape(I1,prod(size(I1)),1),binI); +subplot(3,1,2);hist(reshape(I2,prod(size(I2)),1),binI); +subplot(3,1,3);hist(reshape(I3,prod(size(I3)),1),binI); + + +If1 = filter_output(I1,sigs,szs); +If2 = filter_output(I2,sigs,szs); +If3 = filter_output(I3,sigs,szs); + +I1a = cutoff(I1,5); If1 = cutoff(If1,5); +I2a = cutoff(I2,5); If2 = cutoff(If2,5); +I3a = cutoff(I3,5); If3 = cutoff(If3,5); + + + +figure(4); +bint = [-0.15:0.02:0.15]; +id = 4; + +If = If1; +for j=1:5, + subplot(5,2,2*(j-1)+1); + hist(reshape(If(:,:,id,j)./s1,prod(size(If(:,:,id,j))),1),bint); +end + +If = If2; +for j=1:5, + subplot(5,2,2*j); + hist(reshape(If(:,:,id,j)./s2,prod(size(If(:,:,id,j))),1),bint); +end + + +%%% make 2d histogram bin +figure(5); +idmax = 5; +filt_id = 4; + +for id=1:idmax, + + subplot(idmax,3,(id-1)*3+1); + h2d1 = hist_I_f(I1a,If1(:,:,filt_id,id),binI,bintex); + imagesc(h2d1);axis('image') + subplot(idmax,3,(id-1)*3+2); + h2d2 = hist_I_f(I2a,If2(:,:,filt_id,id),binI,bintex); + imagesc(h2d2);axis('image') + + subplot(idmax,3,id*3); + imagesc(h2d2/sum(sum(h2d2)) + h2d1/sum(sum(h2d1)));axis('image') + colorbar +end + +%%%%%%%%%%%%%%%%%%%%% three types %%%%%%%% +figure(4); +idmax = 5; +filt_id = 2; + +width = 4; + +for id=1:idmax, + + subplot(idmax,width,(id-1)*width+1); + h2d1 = hist_I_f(I1a,If1(:,:,filt_id,id),binI,bintex); + h2d1 = h2d1/sum(sum(h2d1)); + imagesc(h2d1);axis('image'); + + subplot(idmax,width,(id-1)*width+2); + h2d2 = hist_I_f(I2a,If2(:,:,filt_id,id),binI,bintex); + h2d2 = h2d2/sum(sum(h2d2)); + imagesc(h2d2);axis('image') + + subplot(idmax,width,(id-1)*width+3); + h2d3 = hist_I_f(I3a,If3(:,:,filt_id,id),binI,bintex); + h2d3 = h2d3/sum(sum(h2d3)); + imagesc(h2d3);axis('image') + + subplot(idmax,width,id*width); + imagesc(h2d1+h2d2+h2d3);axis('image') + colorbar +end + + +%%%%%%%%%%%% smaller window %%%% +hw = round(4*sigs(1)); + +figure(5);%imagesc(I1a);axis('image'); +cs = round(ginput(1)); + +J = get_win(I1a,cs,[hw,hw]);figure(7);imagesc(J);axis('image'); + +Jf = get_win5(If1,cs,[hw,hw]); +scales = 1:5; nscales = length(scales); +filters = 1:7; nfilters = length(filters); + +figure(8); +for j=1:nscales, + for k=1:nfilters, + subplot(nscales,nfilters,(j-1)*nfilters+k); + h2d = hist_I_f(J,Jf(:,:,(j-1)*7+k));h2d = h2d/sum(sum(h2d)); + imagesc(h2d);axis('image');colorbar;axis('off'); + end +end + + +if 0, + + figure(3); + cs = ginput(1); + + ws = [15,15]; + J = get_win(I,cs,ws); + figure(6);imagesc(J);axis('image'); + + t1 = get_win5(text_des,cs,ws); + + t1p = abs(t1); + %t1p = abs(t1); + %t1p = t1.*(t1>0); + + figure(2);im5(t1p,5,6); + + t1p = reshape(t1p,size(t1p,1)*size(t1p,2),size(t1p,3))'; + + t1pm = mean(t1p')'; + t1ps = t1p- t1pm*ones(1,size(t1p,2)); + + B = t1ps*t1ps'; + [v,d] = eig(B);d = diag(d); + figure(4);plot(d,'x-'); + + figure(5); + subplot(2,2,1);vid = 30;plot(reshape(v(:,vid),6,5),'x-'); + +end + +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test2.m new file mode 100755 index 0000000..c70446a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test2.m @@ -0,0 +1,220 @@ + +%fnameI = '130056'; +%fnameI = '134013'; +fnameI = '134007'; + +%%%% flags %%%%%%%%% +read_image = 1; + +margin = 10+6; +sigs = [1/sqrt(2),1,sqrt(2),2,2*sqrt(2)]; +r =3; +szs = round(3*r*sigs); + + + +%%% image read %%% +if read_image, + cm = sprintf('I = readpgm(''images/%s.pgm'');',fnameI); + disp(cm); + eval(cm); + + Iw = cutoff(I,margin); + figure(1);imagesc(Iw);axis('image'); +end + +%%%% image crop %%% +figure(1);J = imcrop; +figure(2);imagesc(J);axis('image');drawnow; + +Jf = filter_output(J,sigs,szs); +margin = 5; +Ja = cutoff(J,margin);Jfa = cutoff(Jf,margin); +figure(2);imagesc(Ja);axis('image'); + +figure(3); +imagesc(Jfa(:,:,1,3));axis('image');drawnow; + +Jfb = reshape(Jfa,size(Jfa,1),size(Jfa,2),size(Jfa,3)*size(Jfa,4)); +mag = sum(abs(Jfb),3); + +%%%%%% Joint hist. %%%%%%%%% + +filter_id = 1; +scale = 1; +h2d = hist_I_f(Ja,Jfa(:,:,filter_id,scale)); + +figure(4); +imagesc(h2d/sum(sum(h2d)));axis('image');colorbar;colormap(hot); + + +%%%%%%%%%% Jointe hist of cropped area %%%%% +%%% block 1 +fig_id = 1; +[J3,f3,rect] = crop_im_fil(Ja,Jfa,fig_id); + +filter_id = 1;scale = 1;H1 = hist_I_f(J1,f1(:,:,filter_id,scale)); + + +%%% block 2 +fig_id = 1; +[J2,f2,rect] = crop_im_fil(Ja,Jfa,fig_id); + +filter_id = 1;scale = 1;H2 = hist_I_f(J2,f2(:,:,filter_id,scale)); + + +%%%%% disp result %%%%% + +scales = [1:5]; +filter_ids = [1:7]; + +figure(6);disp_hist2d(J2,f2,scales,filter_ids); + +figure(4);disp_hist2d(J1,f1,scales,filter_ids); + +%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%% smaller window +hw = round(4*sigs(1)); + +figure(2);%imagesc(Ja);axis('image'); +cs = round(ginput(1)); + +J1 = get_win(Ja,cs,[hw,hw]);Jf1 = get_win5(Jfa,cs,[hw,hw]); +figure(4);imagesc(J1);axis('image');drawnow; +scales = [1:5];filter_ids = [1:7]; +figure(9);H2 = disp_hist2d(J1,Jf1,scales,filter_ids); + +figure(6); disp_diff(H2,H2o); + + +%%%%%% difference in the neighbourhood %% +hw = round(4*sigs(1)); +hnb = 3; + +B = compute_diff(Ja,Jfa,hw,hnb); + + +%%%%%%%%%% + +if 0, + +figure(4); +bint = [-0.15:0.02:0.15]; +id = 4; + +If = If1; +for j=1:5, + subplot(5,2,2*(j-1)+1); + hist(reshape(If(:,:,id,j)./s1,prod(size(If(:,:,id,j))),1),bint); +end + +If = If2; +for j=1:5, + subplot(5,2,2*j); + hist(reshape(If(:,:,id,j)./s2,prod(size(If(:,:,id,j))),1),bint); +end + + +%%% make 2d histogram bin +figure(5); +idmax = 5; +filt_id = 4; + +for id=1:idmax, + + subplot(idmax,3,(id-1)*3+1); + h2d1 = hist_I_f(I1a,If1(:,:,filt_id,id),binI,bintex); + imagesc(h2d1);axis('image') + subplot(idmax,3,(id-1)*3+2); + h2d2 = hist_I_f(I2a,If2(:,:,filt_id,id),binI,bintex); + imagesc(h2d2);axis('image') + + subplot(idmax,3,id*3); + imagesc(h2d2/sum(sum(h2d2)) + h2d1/sum(sum(h2d1)));axis('image') + colorbar +end + +%%%%%%%%%%%%%%%%%%%%% three types %%%%%%%% +figure(4); +idmax = 5; +filt_id = 2; + +width = 4; + +for id=1:idmax, + + subplot(idmax,width,(id-1)*width+1); + h2d1 = hist_I_f(I1a,If1(:,:,filt_id,id),binI,bintex); + h2d1 = h2d1/sum(sum(h2d1)); + imagesc(h2d1);axis('image'); + + subplot(idmax,width,(id-1)*width+2); + h2d2 = hist_I_f(I2a,If2(:,:,filt_id,id),binI,bintex); + h2d2 = h2d2/sum(sum(h2d2)); + imagesc(h2d2);axis('image') + + subplot(idmax,width,(id-1)*width+3); + h2d3 = hist_I_f(I3a,If3(:,:,filt_id,id),binI,bintex); + h2d3 = h2d3/sum(sum(h2d3)); + imagesc(h2d3);axis('image') + + subplot(idmax,width,id*width); + imagesc(h2d1+h2d2+h2d3);axis('image') + colorbar +end + + +%%%%%%%%%%%% smaller window %%%% +hw = round(4*sigs(1)); + +figure(5);%imagesc(I1a);axis('image'); +cs = round(ginput(1)); + +J = get_win(I1a,cs,[hw,hw]);figure(7);imagesc(J);axis('image'); + +Jf = get_win5(If1,cs,[hw,hw]); +scales = 1:5; nscales = length(scales); +filters = 1:7; nfilters = length(filters); + +figure(8); +for j=1:nscales, + for k=1:nfilters, + subplot(nscales,nfilters,(j-1)*nfilters+k); + h2d = hist_I_f(J,Jf(:,:,(j-1)*7+k));h2d = h2d/sum(sum(h2d)); + imagesc(h2d);axis('image');colorbar;axis('off'); + end +end + + +if 0, + + figure(3); + cs = ginput(1); + + ws = [15,15]; + J = get_win(I,cs,ws); + figure(6);imagesc(J);axis('image'); + + t1 = get_win5(text_des,cs,ws); + + t1p = abs(t1); + %t1p = abs(t1); + %t1p = t1.*(t1>0); + + figure(2);im5(t1p,5,6); + + t1p = reshape(t1p,size(t1p,1)*size(t1p,2),size(t1p,3))'; + + t1pm = mean(t1p')'; + t1ps = t1p- t1pm*ones(1,size(t1p,2)); + + B = t1ps*t1ps'; + [v,d] = eig(B);d = diag(d); + figure(4);plot(d,'x-'); + + figure(5); + subplot(2,2,1);vid = 30;plot(reshape(v(:,vid),6,5),'x-'); + +end + +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test3.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test3.m new file mode 100755 index 0000000..040ed3d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test3.m @@ -0,0 +1,4 @@ +fn = 'lightsmall.ppm'; +nr = Ipara(1);nc = Ipara(2); + +k = 1;imagesc(reshape(v(:,k).*D,nc,nr)');colorbar \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_best_cut.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_best_cut.m new file mode 100755 index 0000000..e04c6ff --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_best_cut.m @@ -0,0 +1,12 @@ + +fn_base = '134035'; +ev_id = 4; +para = [12 ev_id nr nc 100]; +Gmask = ones(nr,nc); +threshold = find_cutpoint(ncutv(:,ev_id),cmapg,12); +threshold = threshold(1:end-1); + +cut_threshold = find_bst_cut(fn_base,para,threshold,Gmask); + +figure(8);ims(ncutv(:,ev_id)0.2*max(max(abs(tmp))))); + + tmp = vs(idx,1:3)*vs(:,1:3)';tmp = reshape(tmp,size(Is,1),size(Is,2)); + subplot(3,2,3); + im(abs(tmp));colorbar; + subplot(3,2,4); im((Is+0.5).*(abs(tmp)>0.2*max(max(abs(tmp))))); + + tmp = vs(idx,1:5)*vs(:,1:5)'; + tmp = reshape(tmp,size(Is,1),size(Is,2)); + subplot(3,2,5); + im(abs(tmp));colorbar + subplot(3,2,6); im((Is+0.5).*(abs(tmp)>0.2*max(max(abs(tmp))))); + +end + + +%%%%%%%%%% +test_tmp = 0; +if test_tmp, +x = -10:0.02:20; +sig = 4; +d = exp(-(x.^2)/sig); +figure(2);plot(x,d); + +ers = []; +for j=0:0.5:10, + d1 = exp(-(x-j).^2/sig); + hold on + plot(x,d1,'r'); + ers = [ers,sum((d1-d).^2)]; +end +hold off; + +figure(1);plot(ers(end)-ers); + + + + + + +end + +%%%%%%%%%%%%%%%%%%% + + +fvs = colize(ts,Is); + +nf = 24;np = 0.5*7442; +hb.sigs = 0.02*ones(1,nf); +hb.bmins= -0.6*ones(1,nf); +hb.bmaxs= 0.6*ones(1,nf); +hb.nbins= 20*ones(1,nf); + +%fh = colize_hist(fvs(1:nf,1:10:end),hb); + +fh2 = hist_inner(fvs(1:nf,1:np),hb); + +[u,d] = eigs(fh2,60); d = diag(d); + +%%%%%%%%%%%% +figure(12); + ct = round(ginput(1)); + idx = (ct(:,1)-1)*size(Is,1) + ct(:,2); + +dist = dist_pair(idx,fvs(1:nf,:),hb); +figure(4); +im(reshape(dist,size(Is,1),size(Is,2)));colorbar + + +%%%%%%%%% +figure(12); + ct = round(ginput(1)); + idx = (ct(:,1)-1)*size(Is,1) + ct(:,2); + +a = colize_hist(fvs(1:nf,idx'),hb); + +figure(5); +cl = 'brgm'; +for j=1:length(idx); + plot(a(:,j),cl(j)); + hold on; +end +hold off + +%%%%%%%%%%% + +%% use chanked feature vectors + +chank_size = 1000; +fname = 'st'; +histbin_fv_chank(fvs(1:nf,:),hb,chank_size,fname); + + +covfh2 = hist_in_chank(fvs(1:nf,:),chank_size,hb.nbins(1)); +[u2,d2] = eigs(covfh2,60); d2 = diag(d2); + +figure(4); +semilogy(d,'p-'); + +figure(3);imagesc(u); + +back_v = backproj_outer_chank(fvs,u,d,chank_size); + +back_v2 = backproj_outer_chank2(fvs,u,d,chank_size); + + +%%%%%%%%%% +figure(2); +for j = 1:16, + subplot(4,4,j); + im(reshape(back_v(:,j),size(Is,1),size(Is,2))); + axis('off');title(num2str(j)); +end + +binv = linspace(-0.6,0.6,20); + +figure(4); +for j=1:16, + subplot(4,4,j); + imagesc(reshape(u(:,j),20,24));title(num2str(j));drawnow; +end + + +figure(6); +for j=1:16, + subplot(4,4,j); + plot(binv,(reshape(u(:,j),20,24)));title(num2str(j));drawnow; +end + + +%%%%%%%%%% +figure(12); + ct = round(ginput(1)); + idx = (ct(:,1)-1)*size(Is,1) + ct(:,2); + +figure(5); +for j = 1:7*2, + subplot(7,2,j); + nvv = 2*j; + dist = back_v(idx,1:nvv)*back_v(:,1:nvv)'; + im(reshape(abs(dist).^2,size(Is,1),size(Is,2)));colorbar + axis('off');title(num2str(nvv)); +end + + +a = colize_hist(fvs(1:nf,idx'),hb)'; + +dist_raw = dist_pair_chank(a,fvs,chank_size); +figure(3);im(reshape(dist_raw.^2,size(Is,1),size(Is,2))); + + + +%%%%%%%%%%%%%% +figure(12); + ct_t3 = round(ginput(5)); + idx_t3 = (ct_t3(:,1)-1)*size(Is,1) + ct_t3(:,2); + +a1 = colize_hist(fvs(1:nf,idx_t1'),hb)'; +a2 = colize_hist(fvs(1:nf,idx_t2'),hb)'; +a3 = colize_hist(fvs(1:nf,idx_t3'),hb)'; + + +%%%%%%%%%%% +figure(1); +for j=1:9, + subplot(3,3,j); + hist(back_v(:,j)) +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex2.m new file mode 100755 index 0000000..9e1d3e7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex2.m @@ -0,0 +1,136 @@ + +%%%%%%%%% test histogram on gray levels %%%%%%%%%%%%% + +%load st +%fvs = colize(Is,Is); + +nf = 1;np = 7442;nbins = 10; + +hb.sigs = 0.02*ones(1,nf); +hb.bmins= 0*ones(1,nf); +hb.bmaxs= 1*ones(1,nf); +hb.nbins= nbins*ones(1,nf); + +fh = colize_hist(fvs(1:nf,1:np),hb); + +fh_inner = fh*fh'; + +nv = nbins-1; + +[u,d] = eigs(fh_inner,nv); d = diag(d); + +figure(6); +for j=1:nv, + subplot(4,4,j); + plot(u(:,j)); + title(num2str(j)); +end + +s = 1./sqrt(d); + +back_v = (fh'*u(:,1:nv)).*(ones(np,1)*s(1:nv)'); + +figure(7); +for j=1:nv, + subplot(4,4,j); + im(reshape(back_v(:,j),size(Is,1),size(Is,2)));axis('off'); + title(num2str(j)); +end + +figure(1); +plot(d,'p-'); +figure(2); +im(u); + + +%%%%%%%%% try the joint x-I histogram bin %%%%%%%%%%%%% + +x = [1:size(Is,1)]'*ones(1,size(Is,2)); +x = reshape(x,size(Is,1),size(Is,2)); + +joint_f(:,:,1) = x; +joint_f(:,:,2) = Is; + +fvs = colize(joint_f,Is); + +nf = 2;np = 7442;nbins = [5,10]; + +hb.sigs = [4,0.02].*ones(1,nf); +hb.bmins= [1,0].*ones(1,nf); +hb.bmaxs= [size(Is,1),1].*ones(1,nf); +hb.nbins= nbins.*ones(1,nf); + +fh = colize_joint_hist(fvs,hb); +fh = reshape(fh,50,np); + +fh_inner = fh*fh'; + +nv = 30; + +[u,d] = eigs(fh_inner,nv); d = diag(d); + +figure(3); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(u(:,j),5,10));axis('off'); + title(num2str(j)); +end + +s = 1./sqrt(d); + +back_v = (fh'*u(:,1:nv)).*(ones(np,1)*s(1:nv)'); + +figure(4); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(back_v(:,j),size(Is,1),size(Is,2)));axis('off'); + title(num2str(j)); +end + + +%%%%%%%% + + +joint_f = []; + +joint_f(:,:,1) = Is; +joint_f(:,:,2) = ts(:,:,1); + +fvs = colize(joint_f,Is); + +nf = 2;np = 7442;nbins = [10,10]; + +hb.sigs = [0.02,0.02].*ones(1,nf); +hb.bmins= [0,-0.6].*ones(1,nf); +hb.bmaxs= [1,0.6].*ones(1,nf); +hb.nbins= nbins.*ones(1,nf); + +fh = colize_joint_hist(fvs,hb); + +fh = reshape(fh,size(fh,1)*size(fh,2),np); + +fh_inner = fh*fh'; + +nv = 30; + +[u,d] = eigs(fh_inner,nv); d = diag(d); + +figure(3); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(u(:,j),10,10));axis('off'); + title(num2str(j)); +end + +s = 1./sqrt(d); + +back_v = (fh'*u(:,1:nv)).*(ones(np,1)*s(1:nv)'); + +figure(4); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(back_v(:,j),size(Is,1),size(Is,2)));axis('off'); + title(num2str(j)); +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex3.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex3.m new file mode 100755 index 0000000..2f245ec --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex3.m @@ -0,0 +1,169 @@ +%%%%%%%%% test histogram on gray levels %%%%%%%%%%%%% + +%load st + +nf = 24;np = 7442;nbins = 10; +fvs = colize(ts(:,:,1:nf),Is); + +hb.sigs = 0.02*ones(1,nf); +hb.bmins= -0.6*ones(1,nf); +hb.bmaxs= 0.6*ones(1,nf); +hb.nbins= nbins*ones(1,nf); + +fh = colize_hist(fvs(1:nf,1:np),hb); + +nw = 4; +fhs = colize_histneighb(fh,Is,nw); + +%%%%%%%%%%%%%%%%%% +figure(12); + ct = round(ginput(1)); + idx = (ct(:,1)-1)*size(Is,1) + ct(:,2); + +figure(1); +subplot(1,2,1); +imagesc(reshape(fhs(:,idx),nbins,nf)) +subplot(1,2,2); +imagesc(reshape(fh(:,idx),nbins,nf)) +%%%%%%%%%% + +fh = fhs; +fhs = sqrt(fhs); + +fh_inner = fhs*fhs'; + +nv = 30; + +[u,d] = eigs(fh_inner,nv); d = diag(d); + +figure(3); +for j=1:min(16,nv), + subplot(4,4,j); + %plot(u(:,j)); + im(reshape(u(:,j),nbins,nf)); + title(num2str(j)); +end + +s = 1./sqrt(d); + +back_v = (fhs'*u(:,1:nv)).*(ones(np,1)*s(1:nv)'); + +figure(4); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(back_v(:,j),size(Is,1),size(Is,2)));axis('off'); + title(num2str(j)); +end + +figure(1); +semilogy(d,'p-'); +%figure(2);imagesc(u); + +%%%%%%%%% +figure(12); + ct = round(ginput(1)); + idx = (ct(:,1)-1)*size(Is,1) + ct(:,2); + +figure(5); +for j = 1:min(14,nv), + subplot(7,2,j); + nvv = j; + dist = back_v(idx,1:nvv)*back_v(:,1:nvv)'; + im(reshape(abs(dist).^2,size(Is,1),size(Is,2)));colorbar + axis('off');title(num2str(nvv)); +end + + + + +%%%%%%%%% try the joint x-I histogram bin %%%%%%%%%%%%% + +x = [1:size(Is,1)]'*ones(1,size(Is,2)); +x = reshape(x,size(Is,1),size(Is,2)); + +joint_f(:,:,1) = x; +joint_f(:,:,2) = Is; + +fvs = colize(joint_f,Is); + +nf = 2;np = 7442;nbins = [5,10]; + +hb.sigs = [4,0.02].*ones(1,nf); +hb.bmins= [1,0].*ones(1,nf); +hb.bmaxs= [size(Is,1),1].*ones(1,nf); +hb.nbins= nbins.*ones(1,nf); + +fh = colize_joint_hist(fvs,hb); +fh = reshape(fh,50,np); + +fh_inner = fh*fh'; + +nv = 30; + +[u,d] = eigs(fh_inner,nv); d = diag(d); + +figure(3); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(u(:,j),5,10));axis('off'); + title(num2str(j)); +end + +s = 1./sqrt(d); + +back_v = (fh'*u(:,1:nv)).*(ones(np,1)*s(1:nv)'); + +figure(4); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(back_v(:,j),size(Is,1),size(Is,2)));axis('off'); + title(num2str(j)); +end + + +%%%%%%%% + + +joint_f = []; + +joint_f(:,:,1) = Is; +joint_f(:,:,2) = ts(:,:,1); + +fvs = colize(joint_f,Is); + +nf = 2;np = 7442;nbins = [10,10]; + +hb.sigs = [0.02,0.02].*ones(1,nf); +hb.bmins= [0,-0.6].*ones(1,nf); +hb.bmaxs= [1,0.6].*ones(1,nf); +hb.nbins= nbins.*ones(1,nf); + +fh = colize_joint_hist(fvs,hb); + +fh = reshape(fh,size(fh,1)*size(fh,2),np); + +fh_inner = fh*fh'; + +nv = 30; + +[u,d] = eigs(fh_inner,nv); d = diag(d); + +figure(3); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(u(:,j),10,10));axis('off'); + title(num2str(j)); +end + +s = 1./sqrt(d); + +back_v = (fh'*u(:,1:nv)).*(ones(np,1)*s(1:nv)'); + +figure(4); +for j=1:min(16,nv), + subplot(4,4,j); + im(reshape(back_v(:,j),size(Is,1),size(Is,2)));axis('off'); + title(num2str(j)); +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex4.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex4.m new file mode 100755 index 0000000..61f1307 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex4.m @@ -0,0 +1,353 @@ + +setup_flag = 0; +cut_window_flag = 0; +run_flag = 0; +other_flag = 0; +test_flag = 1; + + +%%%%%%%%%%%%%%%%% +if setup_flag == 1, +% = readpgm('images/134035.pgm'); + +load st3 + +I_max = 255; +tex_max = 40; + +I2 = min(1,I2/I_max); +t2 = t2/tex_max; +t2 = t2.*(t2<=1) + 1*(t2>1); +t2 = t2.*(t2>=-1) + (-1)*(t2<-1); + + +end + +%%%%%%%%%% + +%% for a given sampling rate, get the index for window center +%% + +[nr,nc] = size(I2); + +hw = 3; +st_sz = 2*hw + 1; + +nr_chank = floor(nr/st_sz); +nc_chank = floor(nc/st_sz); + +id_chank = []; +for k=1+hw:st_sz:nc-hw, + for j=1+hw:st_sz:nr-hw, + id = j+(k-1)*nr; + id_chank = [id_chank,id]; + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F1 difference %%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%% + +fvs = 2*I2(:)'; fvs = fvs -1; + +nf = 1; +hb.sigs = 0.02*ones(1,nf); +hb.bmins= -1*ones(1,nf); +hb.bmaxs= 1*ones(1,nf); +hb.nbins= 10*ones(1,nf); + +fh = colize_hist(fvs(1:nf,:),hb); +fhs = colize_histnb_s(fh,I2,nw,hw); + +A = fhs'*fhs; +figure(1);im(A);colorbar; + +B = A; + +%% display %%% +figure(12); +ct = round(ginput(1)); +ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; + +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(3); +im(reshape(A(idx,:),nr_chank,nc_chank));colorbar; + + + +%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F2 difference %%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%% + +nw = 4;hw =3; + +tnf = size(t2,3); +fst = 1; +r_id = 1; +for j=1:fst:tnf, + nf = fst; + hb.sigs = 0.02*ones(1,nf); hb.bmins= -1*ones(1,nf); + hb.bmaxs= 1*ones(1,nf); hb.nbins= 10*ones(1,nf); + + fvs = colize(t2(:,:,j:j+fst-1),I2); + fh = colize_hist(fvs,hb); + fhs = colize_histnb_s(fh,I2,nw,hw); + A = fhs'*fhs; + cm = sprintf('save F%d A fhs',r_id+1); + disp(cm);eval(cm); + clear fh; + + B = B + A; + + clear A; + + + r_id = r_id +1; +end + + +%%%%%% debug + display %%%%%%%% + +figure(6); +for j=2:30, + subplot(5,6,j); + im(t2(:,:,j-1));axis('off');title(num2str(j-1)); +end +subplot(5,6,1);im(I2);axis('off'); + + +figure(6); +B = zeros(size(A)); +for j = 1:31, + %subplot(5,6,j); + cm = sprintf('load F%d;',j); + disp(cm);eval(cm); + + fhs1 = sqrt(fhs); A = fhs1'*fhs1; +% im(reshape(A(idx,:),nr_chank,nc_chank));axis('off');title(num2str(j-1));colorbar; + B = B+A; +end + + +%%%%%% disp dist. %%%%%% +figure(12); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; + +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(2); +im(reshape(B(idx,:),nr_chank,nc_chank));axis('off');title('B');colorbar; + + + +%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F3 features %%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%% Joint Intensity with filters %%%% + + +tnf = size(t2,3); + +plaatjeon = 1; +if plaatjeon, + for j=7:tnf, + cm = sprintf('!touch /disks/plaatje/scratch/jshi/FJ%d.mat',j); + disp(cm); + eval(cm);cm = sprintf('!ln -s /disks/plaatje/scratch/jshi/FJ%d.mat .',j); + disp(cm);eval(cm); + end +else + for j=1:1, + cm = sprintf('!touch ~/store/st/FJ%d.mat',j); + disp(cm);eval(cm); + cm = sprintf('!ln -s ~/store/st/FJ%d.mat .',j); + disp(cm);eval(cm); + end +end + +for j=7:tnf, + nf = 2; + hb.sigs = 0.02*ones(1,nf); hb.bmins= -1*ones(1,nf); + hb.bmaxs= 1*ones(1,nf); hb.nbins= 10*ones(1,nf); + + fvs = colize(cat(3,t2(:,:,j),I2)); + + fhs = colize_histnb_sf(fvs,I2,hb,nw,hw); + fhs = sqrt(fhs); + A = fhs'*fhs; + cm = sprintf('save FJ%d A fhs',j); + disp(cm);eval(cm); + +end + +%%%% reload data %%%%%%%%%%%%%% +B = zeros(size(A)); + +figure(3); + +for j=1:tnf, + cm = sprintf('load FJ%d;',j); + disp(cm);eval(cm); + + subplot(5,6,j); + im(reshape(A(idx,:),nr_chank,nc_chank));axis('off');title(num2str(j)); + + B = B + A; +end + +figure(2);im(reshape(B(idx,:),nr_chank,nc_chank));axis('off');title('B'); + + +%%%%%% disp dist. %%%%%% +figure(12); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(2); +im(reshape(A(idx,:),nr_chank,nc_chank));axis('off');colorbar; + +%%%%%% disp Joint Hist %%%%%%%%% + +figure(12); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(1); +im(reshape(fhs(:,idx),10,10));axis('off');colorbar; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F4: Joint filters %%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +tnf = size(t2,3); + +nw = 4;hw =3; + +for scale=1:5, + for angle = 1:3, + cm = sprintf('!touch /disks/plaatje/scratch/jshi/FFJ_%d_%d_%d_%d.mat',angle,angle+3,scale,scale); + disp(cm);eval(cm); + cm = sprintf('!ln -s /disks/plaatje/scratch/jshi/FFJ_%d_%d_%d_%d.mat .',angle,angle+3,scale,scale); + disp(cm);eval(cm); + end +end + + +for scale = 1:5, + for angle = 1:3, + nf = 2; + hb.sigs = 0.02*ones(1,nf); hb.bmins= -1*ones(1,nf); + hb.bmaxs= 1*ones(1,nf); hb.nbins= 10*ones(1,nf); + + fvs = colize(cat(3,t2(:,:,(scale-1)*6+angle),... + t2(:,:,(scale-1)*6+angle+3))); + + fhs = colize_histnb_sf(fvs,I2,hb,nw,hw); + fhs = sqrt(fhs); + A = fhs'*fhs; + cm = sprintf('save FFJ_%d_%d_%d_%d A fhs',angle,angle+3,scale,scale); + disp(cm);eval(cm); + end +end + + +%%%%%%%%% load results %%%%%%%%%%% +%B = zeros(size(A)); + +figure(3); +for scale=1:5, + for angle = 1:3, + cm = sprintf('load FFJ_%d_%d_%d_%d.mat',angle,angle+3,scale,scale); + disp(cm);eval(cm); + + subplot(3,5,scale+(angle-1)*5); + im(reshape(A(idx,:),nr_chank,nc_chank)); + axis('off');title(sprintf('%d-%d,%d',angle,angle+3,scale)); + + %B = B + A; + end +end + + + + +%%% disp results + +angle = 1;scale = 1; +cm = sprintf('load FFJ_%d_%d_%d_%d.mat',angle,angle+3,scale,scale); +disp(cm);eval(cm); + + + +figure(12); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +%figure(1);im(reshape(fhs(:,idx),10,10));axis('off');%colorbar; +%figure(2);im(reshape(A(idx,:),nr_chank,nc_chank));%axis('off');%title('B'); +figure(4);im(reshape(B(idx,:),nr_chank,nc_chank));%axis('off');%title('B'); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%% reduction %%%%%%%%%%%%%%%%% +nv = 50; +[uB,dB] = eigs(B,nv);dB = diag(dB); + +figure(1);subplot(2,1,1);plot(dB,'p-'); +subplot(2,1,2);semilogy(dB,'p-'); + +figure(2); + +for j=1:20, + subplot(4,5,j); + im(reshape(uB(:,j),nr_chank,nc_chank));axis('off');colorbar;title(num2str(j)); +end + + +%%%%% Ncut without reduction %%%% +[uNu,dNu] = eig_decomp_v5(B,20); + +figure(1);subplot(2,1,1);plot(dNu,'p-'); +subplot(2,1,2);semilogy(dNu,'p-'); + +figure(2); +for j=2:6, + subplot(1,5,j-1); + im(reshape(-uNu(:,j),nr_chank,nc_chank));axis('off');colorbar;title(num2str(j)); +end + +%%%%%% Ncut with reduction %%%%%%%%% +nvv = 6; +B1 = uB(:,1:nvv)*uB(:,1:nvv)'; + + +[uN,dN] = eig_decomp_v5(abs(B1),20); + +figure(1);subplot(2,1,1);plot(dN,'p-'); +subplot(2,1,2);semilogy(dN,'p-'); + +figure(3); +for j=2:6, + subplot(1,5,j-1); + im(reshape(uN(:,j),nr_chank,nc_chank));axis('off');colorbar;title(num2str(j)); +end + + +%%%%%% + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex5.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex5.m new file mode 100755 index 0000000..2b86e0a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_evtex5.m @@ -0,0 +1,446 @@ + +setup_flag = 0; +cut_window_flag = 0; +run_flag = 0; +other_flag = 0; +test_flag = 1; + + +%%%%%%%%%%%%%%%%% +if setup_flag == 1, + +sigs = [1/sqrt(2),1,sqrt(2),2,2*sqrt(2)];r = 3;szs = round(r*3*sigs); +szs = szs(length(szs))*ones(1,length(szs)); +num_ori = 6; + +compute_flag = 0; +if compute_flag, +fnames = [134002,134007,134011,134013,130065,130038,130039,130040,130042,... + 130045,130046,130056,130068]; + +for j=1:length(fnames), + fname = sprintf('images/%d.pgm',fnames(j)); + + cm = sprintf('!touch /disks/plaatje/scratch/jshi/Fe_%d.mat',fnames(j)); + disp(cm);eval(cm); + + cm = sprintf('!ln -s /disks/plaatje/scratch/jshi/Fe_%d.mat .',fnames(j)); + disp(cm);eval(cm); + + disp(fname); + I = readpgm(fname);figure(3);im(I);title(num2str(fname));drawnow; + [text_des,filters] = compute_filter_fft(I,sigs,r,szs,num_ori); + + cm = sprintf('save Fe_%d text_des filters fname sigs r szs num_ori',fnames(j)); + disp(cm);eval(cm); + + clear text_des filters I +end + +end +else +%%%%%%%%%%%%% + fname = 134013; + + Iname = sprintf('images/%d.pgm',fname); + I = readpgm(Iname); + + cm = sprintf('load Fe_%d.mat',fname); + disp(cm);eval(cm); + + figure(1);im(I); + + + cutsz =20; + I = cutoff(I,cutsz);figure(1);im(I); + text_des = cutoff(text_des,cutsz); + + figure(2); + for j =1:30, + subplot(5,6,j);im(text_des(:,:,j));axis('off'); + end + + I1 = I(20:200,70:240); + T1 = text_des(20:200,70:240,:); + + save st_134013 I1 T1 fname sigs szs r num_ori + +end + + + +%%%%%%%%%%% normalization %%%%%%%%%%% + + +I_max = 250; +tex_max = 40; + +I1 = min(1,I1/I_max); +T1 = T1/tex_max; +T1 = T1.*(T1<=1) + 1*(T1>1); +T1 = T1.*(T1>=-1) + (-1)*(T1<-1); + + +end + +%%%%%%%%%% + +%% for a given sampling rate, get the index for window center +%% + +[nr,nc] = size(I1); + +hw = 3; +st_sz = 2*hw + 1; + +nr_chank = floor(nr/st_sz); +nc_chank = floor(nc/st_sz); + +id_chank = []; +for k=1+hw:st_sz:nc-hw, + for j=1+hw:st_sz:nr-hw, + id = j+(k-1)*nr; + id_chank = [id_chank,id]; + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F1 difference %%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%% + +fvs = 2*I1(:)'; fvs = fvs -1; + +nf = 1; +hb.sigs = 0.02*ones(1,nf); +hb.bmins= -1*ones(1,nf); +hb.bmaxs= 1*ones(1,nf); +hb.nbins= 10*ones(1,nf); +nw = 4;hw =3; + +fh = colize_hist(fvs(1:nf,:),hb); +fhs = colize_histnb_s(fh,I1,nw,hw); + +fhs = sqrt(fhs); +A = fhs'*fhs; +figure(2);im(A);colorbar; + +B = A; + +%% display %%% +figure(2); +ct = round(ginput(1)); +ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; + +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(3);im(reshape(A(idx,:),nr_chank,nc_chank));colorbar; + +subplot(1,2,1);im(reshape(A1(idx,:),nr_chank,nc_chank));colorbar; +subplot(1,2,2);im(reshape(A2(idx,:),nr_chank,nc_chank));colorbar; + + +%%%%%%%%%% +save_flag = 0; + +fn = 134013; + +if save_flag, + cm = sprintf('save F1_%d fhs hw nw nr_chank nc_chank',fn); + disp(cm);eval(cm); + +end + +load_flag = 1; +if load_flag, + cm = sprintf('load F1_%d',fn); + disp(cm);eval(cm); + + A=fhs'*fhs; +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F2 difference %%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%% + +nw = 4;hw =3; + +tnf = size(T1,3); +fst = 1; + +for j=1:fst:1, + nf = fst; + hb.sigs = 0.02*ones(1,nf); hb.bmins= -1*ones(1,nf); + hb.bmaxs= 1*ones(1,nf); hb.nbins= 15*ones(1,nf); + + fvs = colize(T1(:,:,j:j+fst-1),I1); + fh = colize_hist(fvs,hb); + fhs = colize_histnb_s(fh,I1,nw,hw); + fhs = sqrt(fhs); + + A = fhs'*fhs; + + cm = sprintf('save F2_%d_%d fhs hw nw nr_chank nc_chank',j,fn); + disp(cm);eval(cm); + clear fh; + + B = B + A; + + clear A; + +end + + +%%%%%% debug + display %%%%%%%% + +figure(6); +for j=2:30, + subplot(5,6,j); + im(T1(:,:,j-1));axis('off');title(num2str(j-1)); +end +subplot(5,6,1);im(I1);axis('off'); + + +figure(6); +B = zeros(size(A)); +for j = 1:31, + %subplot(5,6,j); + cm = sprintf('load F%d;',j); + disp(cm);eval(cm); + + fhs1 = sqrt(fhs); A = fhs1'*fhs1; +% im(reshape(A(idx,:),nr_chank,nc_chank));axis('off');title(num2str(j-1));colorbar; + B = B+A; +end + + +%%%%%% disp dist. %%%%%% +weight= 5; +A = weight*B+B2; + +figure(1); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(2); +im(reshape(A(idx,:),nr_chank,nc_chank));axis('off');colorbar; %title('B'); +%figure(3); + + +save_flag = 0; +if save_flag , + B2 = B; + save tmp B2 nr_chank nc_chank +end + +%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F3 features %%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%% Joint Intensity with filters %%%% + + +tnf = size(T1,3); + +plaatjeon = 1; +if plaatjeon, + for j=7:tnf, + cm = sprintf('!touch /disks/plaatje/scratch/jshi/FJ%d.mat',j); + disp(cm); + eval(cm);cm = sprintf('!ln -s /disks/plaatje/scratch/jshi/FJ%d.mat .',j); + disp(cm);eval(cm); + end +else + for j=1:1, + cm = sprintf('!touch ~/store/st/FJ%d.mat',j); + disp(cm);eval(cm); + cm = sprintf('!ln -s ~/store/st/FJ%d.mat .',j); + disp(cm);eval(cm); + end +end + +for j=7:tnf, + nf = 2; + hb.sigs = 0.02*ones(1,nf); hb.bmins= -1*ones(1,nf); + hb.bmaxs= 1*ones(1,nf); hb.nbins= 10*ones(1,nf); + + fvs = colize(cat(3,T1(:,:,j),I1)); + + fhs = colize_histnb_sf(fvs,I1,hb,nw,hw); + fhs = sqrt(fhs); + A = fhs'*fhs; + cm = sprintf('save FJ%d A fhs',j); + disp(cm);eval(cm); + +end + +%%%% reload data %%%%%%%%%%%%%% +B = zeros(size(A)); + +figure(3); + +for j=1:tnf, + cm = sprintf('load FJ%d;',j); + disp(cm);eval(cm); + + subplot(5,6,j); + im(reshape(A(idx,:),nr_chank,nc_chank));axis('off');title(num2str(j)); + + B = B + A; +end + +figure(2);im(reshape(B(idx,:),nr_chank,nc_chank));axis('off');title('B'); + + +%%%%%% disp dist. %%%%%% +figure(12); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(2); +im(reshape(A(idx,:),nr_chank,nc_chank));axis('off');colorbar; + +%%%%%% disp Joint Hist %%%%%%%%% + +figure(12); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +figure(1); +im(reshape(fhs(:,idx),10,10));axis('off');colorbar; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%% F4: Joint filters %%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +tnf = size(T1,3); + +nw = 4;hw =3; + +for scale=1:5, + for angle = 1:3, + cm = sprintf('!touch /disks/plaatje/scratch/jshi/FFJ_%d_%d_%d_%d.mat',angle,angle+3,scale,scale); + disp(cm);eval(cm); + cm = sprintf('!ln -s /disks/plaatje/scratch/jshi/FFJ_%d_%d_%d_%d.mat .',angle,angle+3,scale,scale); + disp(cm);eval(cm); + end +end + + +for scale = 1:5, + for angle = 1:3, + nf = 2; + hb.sigs = 0.02*ones(1,nf); hb.bmins= -1*ones(1,nf); + hb.bmaxs= 1*ones(1,nf); hb.nbins= 10*ones(1,nf); + + fvs = colize(cat(3,T1(:,:,(scale-1)*6+angle),... + T1(:,:,(scale-1)*6+angle+3))); + + fhs = colize_histnb_sf(fvs,I1,hb,nw,hw); + fhs = sqrt(fhs); + A = fhs'*fhs; + cm = sprintf('save FFJ_%d_%d_%d_%d A fhs',angle,angle+3,scale,scale); + disp(cm);eval(cm); + end +end + + +%%%%%%%%% load results %%%%%%%%%%% +%B = zeros(size(A)); + +figure(3); +for scale=1:5, + for angle = 1:3, + cm = sprintf('load FFJ_%d_%d_%d_%d.mat',angle,angle+3,scale,scale); + disp(cm);eval(cm); + + subplot(3,5,scale+(angle-1)*5); + im(reshape(A(idx,:),nr_chank,nc_chank)); + axis('off');title(sprintf('%d-%d,%d',angle,angle+3,scale)); + + %B = B + A; + end +end + + + + +%%% disp results + +angle = 1;scale = 1; +cm = sprintf('load FFJ_%d_%d_%d_%d.mat',angle,angle+3,scale,scale); +disp(cm);eval(cm); + + + +figure(12); +ct = round(ginput(1));ct_chank(1) = round((ct(1)-hw-1)/st_sz) + 1; +ct_chank(2) = round((ct(2)-hw-1)/st_sz) + 1; +idx = (ct_chank(:,1)-1)*nr_chank + ct_chank(:,2); + +%figure(1);im(reshape(fhs(:,idx),10,10));axis('off');%colorbar; +%figure(2);im(reshape(A(idx,:),nr_chank,nc_chank));%axis('off');%title('B'); +figure(4);im(reshape(B(idx,:),nr_chank,nc_chank));%axis('off');%title('B'); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%% reduction %%%%%%%%%%%%%%%%% +nv = 50; +[uA,dA] = eigs(A,nv);dA = diag(dA); + +figure(4);suAplot(2,1,1);plot(dA,'p-'); +subplot(2,1,2);semilogy(dA,'p-'); + +figure(3); + +for j=1:20, + subplot(4,5,j); + im(reshape(uA(:,j),nr_chank,nc_chank));axis('off');colorbar;title(num2str(j)); +end + + +%%%%% Ncut without reduction %%%% + +[uNu,dNu] = eig_decomp_v5(A,20); + +figure(4);subplot(2,1,1);plot(dNu,'p-'); +subplot(2,1,2);semilogy(dNu,'p-'); + +figure(3); +for j=2:6, + subplot(1,5,j-1); + im(reshape(-uNu(:,j),nr_chank,nc_chank));axis('off');colorbar;title(num2str(j)); +end + +%%%%%% Ncut with reduction %%%%%%%%% +nvv = 7; +A1 = uA(:,1:nvv)*uA(:,1:nvv)'; + + +[uN,dN] = eig_decomp_v5(abs(A1),20); + +figure(1);subplot(2,1,1);plot(dN,'p-'); +subplot(2,1,2);semilogy(dN,'p-'); + +figure(3); +for j=2:6, + subplot(1,5,j-1); + im(reshape(uN(:,j),nr_chank,nc_chank));axis('off');colorbar;title(num2str(j)); +end + + +%%%%%% + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion.m new file mode 100755 index 0000000..91c97f9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion.m @@ -0,0 +1,117 @@ + +im_sz = [40,40]; + +ob_szh = [6,3]; + +ob_c = [15,12]; + +bg_color = 0.2; + +ob_color = 0.8; + +mag = 0.2; + +I_bg = bg_color + mag*randn(im_sz); + +I_obj = ob_color + mag*randn(2*ob_szh+1); + + +w = 3; + +v5 = 1; +Js = []; + +if ~v5, + for j=1:5, + fc = sprintf('J%d = I_bg;',j); + eval(fc); + + fc = sprintf('J%d(ob_c(1)-ob_szh(1):ob_c(1)+ob_szh(1),ob_c(2)-ob_szh(2):ob_c(2)+ob_szh(2)) = I_obj;',j); + eval(fc); + + ob_c = ob_c+[0,2]; + end +else + nf = 4; + for j = 1:nf, + + J = I_bg; + J(ob_c(1)-ob_szh(1):ob_c(1)+ob_szh(1),ob_c(2)-ob_szh(2):ob_c(2)+ob_szh(2)) = I_obj; + + if (j==1), + [gy,gx] = grad(J,w); + end + + ob_c = ob_c+[0,2]; + + Jw = cutoff(J,w); + Js(:,:,j) = Jw; + end + +end + +[nr,nc] = size(gx); + +for j=1:nf, + subplot(1,nf,j); + imagesc(Js(:,:,j));axis('tightequal'); +end + + +writepnm5('test_motion.pnm',Js); +writepnm5('test_motion_gx.pnm',gx); +writepnm5('test_motion_gy.pnm',gy); +%imagesc(J1);colorbar; + + +inpara = [2,5,0.5,1,0.5]; + +[A,D,Ipara] = cas('test_motion',inpara); + +B= A+ A'; +clear A; + +%BB = B(1:19^2,19^2+(1:19^2)); +%imagesc(BB); + +[v,d] = eigs(B);d = diag(d); + +k = 2; + +figure(1); +%nf = 5; + +nr = nr-5; +nc = nc-5; + +n = nr* nc; + +for j =1:nf, + subplot(1,nf,j); + imagesc(reshape(v((j-1)*n+(1:n),k).*D(1:n),nr,nc)');axis('tightequal'); +end + +%%%%% + + +figure(3); +T = readpnm('test_motion.pnm'); +nf = size(T,3); +for j=1:nf, + subplot(1,nf,j); + imagesc(T(:,:,j));axis('tightequal'); +end + + +figure(2); +Gx = readpnm('test_motion_gx.pnm'); + +[nr,nc] =size(Gx); +n = nr*nc; + +imagesc(reshape(B(n+1:2*n,6*nc+7),nc,nr)');colorbar + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion2.m new file mode 100755 index 0000000..2959fa8 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_motion2.m @@ -0,0 +1,127 @@ + +im_sz = [40,40]; + +ob_szh = [4,3]; + +ob_c = [12,12]; + +ob_co = [30,28]; + +bg_color = 0.2; + +ob_color = 0.8; + +mag = 0.2; + +I_bg = bg_color + mag*randn(im_sz); + +I_obj = ob_color + mag*randn(2*ob_szh+1); + + +w = 3; + +v5 = 1; +Js = []; + +if ~v5, + for j=1:5, + fc = sprintf('J%d = I_bg;',j); + eval(fc); + + fc = sprintf('J%d(ob_c(1)-ob_szh(1):ob_c(1)+ob_szh(1),ob_c(2)-ob_szh(2):ob_c(2)+ob_szh(2)) = I_obj;',j); + eval(fc); + + fc = sprintf('J%d(ob_co(1)-ob_szh(1):ob_co(1)+ob_szh(1),ob_co(2)-ob_szh(2):ob_co(2)+ob_szh(2)) = I_obj;',j); + eval(fc); + + ob_c = ob_c+[0,2]; + ob_co = ob_co-[0,2]; + + end +else + nf = 4; + for j = 1:nf, + + J = I_bg; + J(ob_c(1)-ob_szh(1):ob_c(1)+ob_szh(1),ob_c(2)-ob_szh(2):ob_c(2)+ob_szh(2)) = I_obj; + J(ob_co(1)-ob_szh(1):ob_co(1)+ob_szh(1),ob_co(2)-ob_szh(2):ob_co(2)+ob_szh(2)) = I_obj; + + if (j==1), + [gy,gx] = grad(J,w); + end + + ob_c = ob_c+[0,2]; + ob_co = ob_co-[0,2]; + + Jw = cutoff(J,w); + Js(:,:,j) = Jw; + end + +end + +[nr,nc] = size(gx); + +for j=1:nf, + subplot(1,nf,j); + imagesc(Js(:,:,j));axis('tightequal'); +end + + +writepnm5('test_motion.pnm',Js); +writepnm5('test_motion_gx.pnm',gx); +writepnm5('test_motion_gy.pnm',gy); +%imagesc(J1);colorbar; + + +inpara = [2,5,0.5,1,0.5]; + +[A,D,Ipara] = cas('test_motion',inpara); + +B= A+ A'; +clear A; + +%BB = B(1:19^2,19^2+(1:19^2)); +%imagesc(BB); + +[v,d] = eigs(B);d = diag(d); + +k = 2; + +figure(1); +%nf = 5; + +nr = nr-5; +nc = nc-5; + +n = nr* nc; + +for j =1:nf, + subplot(1,nf,j); + imagesc(reshape(v((j-1)*n+(1:n),k).*D(1:n),nr,nc)');axis('tightequal'); +end + +%%%%% + + +figure(3); +T = readpnm('test_motion.pnm'); +nf = size(T,3); +for j=1:nf, + subplot(1,nf,j); + imagesc(T(:,:,j));axis('tightequal'); +end + + +figure(2); +Gx = readpnm('test_motion_gx.pnm'); + +[nr,nc] =size(Gx); +n = nr*nc; + +imagesc(reshape(B(n+1:2*n,6*nc+7),nc,nr)');colorbar + + +%%%%%%%%%%%%% + + +K = zeros(im_sz); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_period.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_period.m new file mode 100755 index 0000000..2994d15 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_period.m @@ -0,0 +1,58 @@ +flag = 2; + +if flag ==1, + +ws = [50,50]; + +figure(1);J = get_win(I,ginput(1),ws); +figure(4);imagesc(J); + +J = J - mean(mean(reshape(J,prod(size(J)),1))); +X = fftshift(fft2(J)); + +figure(3);imagesc(abs(X));colorbar +figure(2);mesh(abs(X)); + +else + +fn = '1.pgm'; + +% spatial gaussian parameter +xscale = 1; + +% half size of the neighbourhood +xnb = 5; + +% setting the the HSV gaussian parameter:[h s v] +Iscale = [0.01]; + +Input_para = [xscale,xnb,Iscale]; + +% compute the lower half the association matrix +[A,D,Ipara] = compute_A_pgm(fn,Input_para); + +nr = Ipara(1);nc = Ipara(2); + +B = A+A'; +clear A; + + +% eigen decompostion +options.tol = 1e-4; +num_eig_v = 10; +fprintf('doing eigs ...\n'); +[v,d] = eigs(B,num_eig_v,options); + +k = 1;imagesc(reshape(v(:,k).*D,nc,nr)');colorbar + + +end + + + + + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_text.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_text.m new file mode 100755 index 0000000..4cc5759 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/test_text.m @@ -0,0 +1,434 @@ + + +%case = 1; + +read_flag = 1; +compute_flag = 0; +load_flag = 0; +decomp_flag = 0; +hist_flag = 0; + +test_real = 0; + + +if read_flag, +if caseid == 1, + ifn = 'images/130049.pgm'; +elseif caseid == 2, + ifn = 'images/130055.pgm'; +elseif caseid == 3, + ifn = 'images/130056.pgm'; +elseif caseid == 4, + ifn = 'images/130057.pgm'; +elseif caseid == 5, + ifn = 'images/130060.pgm'; +elseif caseid == 6, + ifn = 'images/130061.pgm'; +elseif caseid == 7, + ifn = 'images/130062.pgm'; +elseif caseid == 8, + ifn = 'images/130065.pgm'; +elseif caseid == 9, + ifn = 'images/130066.pgm'; +elseif caseid == 10, + ifn = 'images/130068.pgm'; +elseif caseid == 11, + ifn = 'images/130070.pgm'; +else + ifn = 'images/130070.pgm'; +end + +I = readpgm(ifn); +figure(1); +imagesc(I);colormap(gray);drawnow; +axis('tightequal'); + +end + +%%%%% load %%% + +if load_flag, + fn = sprintf('load cresult_%d;',caseid); + eval(fn); +end + + +%%%%%%%%%%%%% compute %%%%%%%%%%% +sig = 0.5; +r = 3; +sz = 15; +Iw = cutoff(I,0.5*sz); +figure(1);imagesc(Iw); +axis('image'); + +if compute_flag, + +as = 0:30:150; + +Cresult = []; + +for j = 1:length(as), + fprintf('.'); + angle = as(j); + + g = doog2( sig,r,angle,sz); + + g = g - mean(reshape(g,prod(size(g)),1)); + + g = g/sum(sum(abs(g))); + + c = conv2(I,g,'valid'); + + Cresult(:,:,j) = c; +end + + +fprintf('\n'); + + +figure(2); + +subplot(2,3,1); +imagesc(Cresult(:,:,1).^2);axis('tightequal');colorbar + +subplot(2,3,2); +imagesc(Cresult(:,:,2).^2);axis('tightequal');colorbar + +subplot(2,3,3); +imagesc(Cresult(:,:,3).^2);axis('tightequal');colorbar + +subplot(2,3,4); +imagesc(Cresult(:,:,4).^2);axis('tightequal');colorbar + +subplot(2,3,5); +imagesc(Cresult(:,:,5).^2);axis('tightequal');colorbar + +subplot(2,3,6); +imagesc(Cresult(:,:,6).^2);axis('tightequal');colorbar + +Cs = []; +Mcs = []; +for j=1:length(as), + Cs(:,:,j) = reduce(reduce(abs(Cresult(:,:,j)))); + + Mcs = [Mcs,max(max(Cs(:,:,j)))]; + +end + +ms = max(Mcs); + +figure(3); +for j=1:6, + fn = sprintf('Cs(:,:,%d) = Cs(:,:,%d)/ms;',j,j); + eval(fn); + fn = sprintf('subplot(2,3,%d);imagesc(Cs(:,:,%d));',j,j); + eval(fn);axis('tightequal');colorbar +end + +fn = sprintf('save cresult_%d.mat Cresult Cs',caseid); +disp(fn); +eval(fn); + +end + +%%%%%%%%%%%%%%%%% decomp %%%%%%%%%%%% + + +if decomp_flag, + +%writepnm5('txt_2.pnm',Cs); + +%writepnm5('130068.pnm',Cs); + + +%I_scale = 0.0025; +%X_scale = 3^2; +%[A,D,Ipara] = compute_A_sparmul2(10,I_scale,X_scale); + + +I_scale = 0.02; +X_scale = 2; +[A,D,Ipara] = compute_A_pnm('130068.pnm',[X_scale,I_scale]); + +nr = Ipara(1);nc = Ipara(2); +imagesc(reshape(D,nc,nr)');colorbar; + +B = A+A';clear A; + +options.tol = 1e-7; + +[v,d] = eigs(B,9,options); + +figure(4); +k = 1; imagesc(reshape(v(:,k).*D,nc,nr)'); + +end + + +%%%% histogram %%%% + +%hist_flag = 1; + +%figure(1);imagesc(Iw);axis('image'); +if hist_flag ==1, + + +ws = [12,12]; + +figure(7); + +cs = ginput(1); + +cs = 10*(cs-1)+w/2; + +%cs(1,:) = w+(floor((cs(1,:)-w)/gap)*gap); +%cs(2,:) = w+(floor((cs(2,:)-w)/gap)*gap); + +J = get_win(Iw,cs(1,:),ws); +Jbar = get_win5(Cresult,cs(1,:),ws); + + +figure(2); +subplot(3,3,1);imagesc(J);colorbar +for j=1:6,subplot(3,3,1+j);imagesc(abs(Jbar(:,:,j)));colorbar; end + +[hists,bins] = get_hist(J,Jbar);show_hist(hists,bins,4); +cumhists = get_cumhist(hists);show_cumhist(cumhists,bins,6,1,'b-o'); + +J2 = get_win(Iw,cs(2,:),ws); +Jbar2 = get_win5(Cresult,cs(2,:),ws); + +figure(3); +subplot(3,3,1);imagesc(J2);colorbar +for j=1:6,subplot(3,3,1+j);imagesc(abs(Jbar2(:,:,j)));colorbar; end + +[hists2,bins2] = get_hist(J2,Jbar2);show_hist(hists2,bins2,5); +cumhists2 = get_cumhist(hists2);show_cumhist(cumhists2,bins2,6,0,'r-*'); + +diff.inten = max(abs(cumhists.inten-cumhists2.inten)); +diff.mag = max(abs(cumhists.mag-cumhists2.mag)); +diff.text = max(max(abs(cumhists.text-cumhists2.text))); + +figure(7); + +disp([diff.inten,diff.mag,diff.text]); +maxdiff = max([diff.inten,diff.mag,diff.text]); +disp(1-sigmoid(diff.inten,0.22,0.02)); + + +if 0, +%A = pair_dist_text(Iw,Cresult,15); + +r =4;w = 22;gap = 5;sig_x= 20.0; +inpara = [r,w,gap,sig_x,0.16,0.2,0.2]; +[Cum,tm] = cAh(Iw,mag,abs(Cresult),inpara); + +[Cum,Nb,Nc] = cAh4(Iw,mag,abs(Cresult),inpara); + + +B = A+ A';clear A; + +figure(1); +c = ginput(1); +cx = floor(c(1)/gap); +cy = floor(c(2)/gap); +[cx,cy] +figure(7) +imagesc(reshape(B(cy*Cum(1)+cx,:),Cum(1),Cum(2))');colorbar + + +cutoff = [0.22,0.2,0.2]; +sig_hist = [0.02,0.04,0.05]; + +inpara2 = [r,5,cutoff,sig_hist]; +[A,D] = compute_A_hist3(tm,Cum,inpara2); + +B = A+A';clear A; +imagesc(reshape(D,Cum(1),Cum(2))'); + + +[v,d] = eigs(B); + + +end + +end + +%%%%%%%%%%%%% trans_texture %%%%%%%%%%%% +trans_text = 0; + + +if trans_text, + figure(1); + cs = ginput(1); + + ws = [40,40]; + + J = get_win(Iw,cs(1,:),ws); + Jbar = get_win5(Cresult,cs(1,:),ws); + Jmag = get_win(mag,cs(1,:),ws); + + figure(3);imagesc(J); + + figure(3); + for j=1:6, + subplot(2,3,j);imagesc(abs(Jbar(:,:,j)));axis('image');colorbar; + end + + f= abs(Jbar(40,38,:)); + g= abs(Jbar(40,47,:)); + + dot(f,g)/max(dot(f,f),dot(g,g)) + + ff = myinterp(f,10); gg = myinterp(g,10); + dot(ff,gg)/max(dot(ff,ff),dot(gg,gg)) + + cum = mc_corr(ff,gg,[-6,6]); + max(cum)/max(dot(f,f),dot(g,g)) + + + center = [40,35]; + + f = squeeze(abs(Jbar(center(1),center(2),:))); + ff = myinterp(f,10); + mag_ff = dot(ff,ff); + mag_c = Jmag(center(1),center(2)); + dy = 0; + + cor_cofs = []; + mags = []; + for dx = -15:15, + g = squeeze(abs(Jbar(center(1)+dy,center(2)+dx,:))); + gg = myinterp(g,10); + + cum = mc_corr(ff,gg,[-6,6]); + cor_cofs = [cor_cofs,max(cum)/max(mag_ff,dot(gg,gg))]; + + mags =[mags,max(mag_c,Jmag(center(1)+dy,center(2)+dx,:))]; + + end + + simulation_on =0; + + if simulation_on, + + sz = [161,161] + SI = zeros(sz); + + for i=2:18:sz(1), + SI(i:i+2,:) = 1+SI(i:i+2,:); + end + + imagesc(SI);axis('image'); + + tmp1 = mimrotate(SI,90,'nearest','crop'); + tmp2 = mimrotate(SI,45,'nearest','crop'); + + ly = round(0.7*sz(1)); + lx = round(0.7*sz(1)); + sy = round(0.16*sz(1)); + sx = round(0.2*sz(2)); + TI = [tmp1(1:ly,1:lx),tmp2(sy+1:sy+ly,sy+1:sy+round(0.4*lx))]; + + TI = TI+0.04*randn(size(TI)); + + %sig = 1/sqrt(2);r = 3;sz = round(r*3*sig); + + sigs = [1/sqrt(2),1,sqrt(2),2,2*sqrt(2)];r = 3;szs = round(r*3*sigs); + [text_des,TIw] = compute_filter(TI,sigs,r,szs); + figure(2);imagesc(TIw);axis('image'); + + figure(3); + im5(abs(text_des),2,3); + + text_des = abs(text_des); + + text_des = T1; + + numband = size(text_des,3); r = 10; + sig_x = 90; sig_inten = 0.15; sig_tex = 0.01;w_inten = 0.03; + para = [numband,r,sig_x,sig_inten,sig_tex,w_inten]; + + [A,D,Ipara] = compute_A_text(TIw,text_des,para); + nr = Ipara(1);nc = Ipara(2); + B = A+A'; clear A; + + figure(2); + cs = ginput(1); + cs = round(cs);id = cs(2)*nc+cs(1); + + figure(4); + imagesc(reshape(B(id,:),nc,nr)');axis('image');colorbar + + [v,d] = eigs(B); + figure(4);imagesc(reshape(D.*v(:,1),nc,nr)');axis('image'); + + end + +end + +if test_real == 1, + sigs = [1/sqrt(2),1,sqrt(2),2,2*sqrt(2)];r = 3;szs = round(r*3*sigs); + text_des = compute_filter(I,sigs,r,szs); + + text_des = abs(text_des); +%save filter_3.mat + + %%%% cutoff margins, + margin = 6+10; + + Iw = cutoff(I,margin); + + T1= reshape(text_des,size(text_des,1),size(text_des,2),size(text_des,3)*size(text_des,4)); + T1 = cutoff(T1,margin); + + %%%%% reduce resolution + + Iwp = compact(Iw,4); + + T1 = reduce_all(T1); + T1 = reduce_all(T1); + +% T1 = T1/70; + + % writepnm5('test6_image.pnm',Iwp);writepnm5('test6_filter.pnm',T1); + + numband = size(T1,3); r = 2; + sig_x = 20; sig_inten = 0.15; sig_tex = 0.01;w_inten = 0.01; + para = [numband,r,sig_x,sig_inten,sig_tex,w_inten]; + + [A,D,Ipara] = compute_A_text(Iwp,T1,para); + nr = Ipara(1);nc = Ipara(2); + figure(4);imagesc(reshape(D,nc,nr)');axis('image'); + drawnow; + + + numband = 6; + r = 5; sig_x = 20.0; + sig_tex = 0.01; w_inten = 0.01; w = 2; + para = [numband,r,sig_x,sig_tex,w_inten,w,size(T1,2)]; + + [A,D,Ipara] = compute_A_text2(Iw,T1(:,:,1:numband)/70,para); + nr = Ipara(1);nc = Ipara(2); + + + + B = A+A'; clear A; + + + figure(2); + cs = ginput(1); + cs = floor(cs/4)+1;id = cs(2)*nc+cs(1); + + figure(4); + imagesc(reshape(B(id,:),nc,nr)');axis('image');colorbar + + +end + + + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp.m new file mode 100755 index 0000000..b932912 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp.m @@ -0,0 +1,68 @@ + +sw = 3; +gap = 2*sw+1; + +nw = 6; + +for j=1:20, + l = max(0,j-1-nw); +% l = max(0,j-1-2*nw); + rs(j) = ceil((l-sw)/gap) + 1; + l = min(20,j-1+nw); +% l = min(20,j-1); + re(j) = floor((l-sw)/gap) +1; +end + +plot([1:20],rs,'p-',[1:20],re,'rp-') + + +%%%%%%%% + +bin_max = 1.0; +bin_min = -1.0; +num_bin = 30; +sig = 0.2; + +data = 0.482; + +inc = (bin_max-bin_min)/num_bin; + +bs = -100; +be = bin_min+inc; +b = []; + +for j=1:num_bin, + + b(j) = tmp1(bs,be,data,sig); + bs = be; + be= be+inc; +end +plot(b,'p-'); + + + +bmin = -1; + +inc = 0.2; +a = 0.1; +b = -1250; +ovs = 625; + +bs = bmin; +be = bs+inc; + +data = -0.482; + +for j=1:10, + tmp = bs-data; + fs = exp(-(tmp*tmp*ovs)); + ks = b*tmp; + + tmp = be-data; + fe = exp(-(tmp*tmp*ovs)); + ke = b*tmp; + + bin(j) = fs*(2+a*ks) + fe*(2-a*ke); + bs = be; + be = be+inc; +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp1.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp1.m new file mode 100755 index 0000000..db5dbc1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp1.m @@ -0,0 +1,25 @@ +function d = tmp1(bs,be,data,sig) + +sig = sig^2; + +if 1, +a = (bs+be)*0.5; +d = (a-bs)*(exp(-(bs-data)^2/sig) + exp(-(a-data)^2/sig)) + ... + (be-a)*(exp(-(a-data)^2/sig) + exp(-(be-data)^2/sig)); +d = d*2/sqrt(pi); +else + +a = (be-bs)/2; + +h1 = exp(-(bs-data)^2/sig); +h2 = exp(-(be-data)^2/sig); + +k1 = -2*(bs-data)/sig; +k2 = -2*(be-data)/sig; + +d = a*(h1*(2+2*a*k1) + h2*(2-2*a*k2)); +d = d*2/sqrt(pi); + +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp2.m new file mode 100755 index 0000000..b361cdc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp2.m @@ -0,0 +1,17 @@ +function d = tmp2(bs,be,data,sig) + +sig = sig^2; + + +a = (be-bs)/2; + +h1 = exp(-(bs-data)^2/sig); +h2 = exp(-(be-data)^2/sig); + +k1 = -2*(bs-data)/sig; +k2 = -2*(be-data)/sig; + +d = (h1*(2+2*a*k1) + h2*(2-2*a*k2)); +%d = a*d*2/sqrt(pi); + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp3.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp3.m new file mode 100755 index 0000000..c1bffd9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/tmp3.m @@ -0,0 +1,126 @@ +function result = erfcore(x,jint) +%ERFCORE Core algorithm for error functions. +% erf(x) = erfcore(x,0) +% erfc(x) = erfcore(x,1) +% erfcx(x) = exp(x^2)*erfc(x) = erfcore(x,2) + +% C. Moler, 2-1-91. +% Copyright (c) 1984-96 by The MathWorks, Inc. +% $Revision: 5.7 $ $Date: 1996/10/28 20:57:59 $ + +% This is a translation of a FORTRAN program by W. J. Cody, +% Argonne National Laboratory, NETLIB/SPECFUN, March 19, 1990. +% The main computation evaluates near-minimax approximations +% from "Rational Chebyshev approximations for the error function" +% by W. J. Cody, Math. Comp., 1969, PP. 631-638. + + if ~isreal(x), + error('Input argument must be real.') + end + result = repmat(NaN,size(x)); +% +% evaluate erf for |x| <= 0.46875 +% + xbreak = 0.46875; + k = find(abs(x) <= xbreak); + if ~isempty(k) + a = [3.16112374387056560e00; 1.13864154151050156e02; + 3.77485237685302021e02; 3.20937758913846947e03; + 1.85777706184603153e-1]; + b = [2.36012909523441209e01; 2.44024637934444173e02; + 1.28261652607737228e03; 2.84423683343917062e03]; + + y = abs(x(k)); + z = y .* y; + xnum = a(5)*z; + xden = z; + for i = 1:3 + xnum = (xnum + a(i)) .* z; + xden = (xden + b(i)) .* z; + end + result(k) = x(k) .* (xnum + a(4)) ./ (xden + b(4)); + if jint ~= 0, result(k) = 1 - result(k); end + if jint == 2, result(k) = exp(z) .* result(k); end + end +% +% evaluate erfc for 0.46875 <= |x| <= 4.0 +% + k = find((abs(x) > xbreak) & (abs(x) <= 2.)); + if ~isempty(k) + c = [5.64188496988670089e-1; 8.88314979438837594e00; + 6.61191906371416295e01; 2.98635138197400131e02; + 8.81952221241769090e02; 1.71204761263407058e03; + 2.05107837782607147e03; 1.23033935479799725e03; + 2.15311535474403846e-8]; + d = [1.57449261107098347e01; 1.17693950891312499e02; + 5.37181101862009858e02; 1.62138957456669019e03; + 3.29079923573345963e03; 4.36261909014324716e03; + 3.43936767414372164e03; 1.23033935480374942e03]; + + y = abs(x(k)); + xnum = c(9)*y; + xden = y; + for i = 1:7 + xnum = (xnum + c(i)) .* y; + xden = (xden + d(i)) .* y; + end + result(k) = (xnum + c(8)) ./ (xden + d(8)); + if jint ~= 2 + z = fix(y*16)/16; + del = (y-z).*(y+z); + result(k) = exp(-z.*z) .* exp(-del) .* result(k); + end + end +% +% evaluate erfc for |x| > 4.0 +% + k = find(abs(x) > 2.0); + if ~isempty(k) +if 0, + p = [3.05326634961232344e-1; 3.60344899949804439e-1; + 1.25781726111229246e-1; 1.60837851487422766e-2; + 6.58749161529837803e-4; 1.63153871373020978e-2]; + q = [2.56852019228982242e00; 1.87295284992346047e00; + 5.27905102951428412e-1; 6.05183413124413191e-2; + 2.33520497626869185e-3]; + + y = abs(x(k)); + z = 1 ./ (y .* y); + xnum = p(6).*z; + xden = z; + for i = 1:4 + xnum = (xnum + p(i)) .* z; + xden = (xden + q(i)) .* z; + end + result(k) = z .* (xnum + p(5)) ./ (xden + q(5)); + result(k) = (1/sqrt(pi) - result(k)) ./ y; + if jint ~= 2 + z = fix(y*16)/16; + del = (y-z).*(y+z); + result(k) = exp(-z.*z) .* exp(-del) .* result(k); + k = find(~isfinite(result)); + result(k) = 0*k; + end +end + result(k) = 0; + end +% +% fix up for negative argument, erf, etc. +% + if jint == 0 + k = find(x > xbreak); + result(k) = (0.5 - result(k)) + 0.5; + k = find(x < -xbreak); + result(k) = (-0.5 + result(k)) - 0.5; + elseif jint == 1 + k = find(x < -xbreak); + result(k) = 2. - result(k); + else % jint must = 2 + k = find(x < -xbreak); + z = fix(x(k)*16)/16; + del = (x(k)-z).*(x(k)+z); + y = exp(z.*z) .* exp(del); + result(k) = (y+y) - result(k); + end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/true_loc.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/true_loc.m new file mode 100755 index 0000000..7bf060f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/true_loc.m @@ -0,0 +1,23 @@ +function a = true_loc(loca,g,scale); + +if ~exist('scale'), + scale = 50; +end + +y = loca(1,:); +x = loca(2,:); + +min_x = min(x); +min_y = min(y); + +x = x - min_x; +y = y - min_y; + +max_x = max(x);max_y = max(y); +min_scale = min(max_x,max_y); + +a(1) = (g(1)-1)*min_scale/(scale); +a(2) = (g(2)-1)*min_scale/(scale); + +a(1) = a(1) + min_x; +a(2) = a(2) + min_y; diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/vmquant.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/vmquant.m new file mode 100755 index 0000000..ab4eb28 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/vmquant.m @@ -0,0 +1,112 @@ +function [im, map] = vmquant(arg1,arg2,arg3,arg4,arg5,arg6,arg7) +%VMQUANT Variance Minimization Color Quantization. +% [X, MAP] = VMQUANT(R,G,B,K,[Qr Qg Qb],DITHER,Qe) or +% VMQUANT(RGB,K,[Qr Qg Qb],DITHER,Qe), where RGB is a 3-D array, +% converts an arbitrary image comprised of RGB triples into an +% indexed image X with color map MAP. K specifies the number +% of desired entries in the target color map, and [Qr Qg Qb] +% specifies the number of quantization bits to assign each color +% axis during color interpolation. DITHER is a string ('dither' or +% 'nodither') that indicates whether or not to perform error propagation +% dither on the output image. Qe specifies the number of bits of +% quantization used in the error calculations. +% +% K is optional and defaults to 256. +% [Qr Qg Qb] is optional and defaults to [5 5 5]. +% DITHER is optional and defaults to 'nodither'. +% Qe is optional and defaults to 8. +% +% See also: RGB2IND, RGB2GRAY, DITHER, IND2RGB, CMUNIQUE, IMAPPROX. + +% This is the wrapper function for the MEX file VMQUANTC.C + +% Joseph M. Winograd 6-93 +% Copyright (c) 1993 by The MathWorks, Inc. +% $Revision: 5.3 $ $Date: 1996/08/22 22:09:03 $ + +% Reference: Xiaolin Wu, "Efficient Statistical Computation for +% Optimal Color Quantization," Graphics Gems II, (ed. James +% Arvo). Academic Press: Boston. 1991. + +if nargin < 1, + error('Not enough input arguments.'); +end + +threeD = (ndims(arg1)==3); % Determine if input includes a 3-D array + +if threeD, + error( nargchk( 1, 5, nargin ) ); + + % NOTE: If you change defaults, change them also + % in VMQUANTC.C and recompile the MEX function. + if nargin < 5 + arg5 = 8; % DEFAULT_QE = 8 + end + + if nargin < 4 + arg4 = 'n'; % DEFAULT_DITHER = 0 + end + + if nargin < 3 + arg3 = [5 5 5]; % DEFAULT_Q = [5 5 5] + end + + if nargin < 2 + arg2 = 256; % DEFAULT_K = 256 + end + + rout = arg1(:,:,1); + g = arg1(:,:,2); + b = arg1(:,:,3); + + if strcmp(lower(arg4(1)),'d') + dith = 1; + else + dith = 0; + end + + arg7 = arg5; + arg5 = arg3; + arg4 = arg2; + +else + error( nargchk( 3, 7, nargin ) ); + + if nargin < 7 + arg7 = 8; % DEFAULT_QE = 8 + end + + if nargin < 6 + arg6 = 'n'; % DEFAULT_DITHER = 0 + end + + if nargin < 5 + arg5 = [5 5 5]; % DEFAULT_Q = [5 5 5] + end + + if nargin < 4 + arg4 = 256; % DEFAULT_K = 256 + end + + rout = arg1; + g = arg2; + b = arg3; + + if strcmp(lower(arg6(1)),'d') + dith = 1; + else + dith = 0; + end + +end + +if (~isa(rout,'uint8')) + rout = uint8(round(255*rout)); +end +if (~isa(g,'uint8')) + g = uint8(round(255*g)); +end +if (~isa(b,'uint8')) + b = uint8(round(255*b)); +end +[im,map] = vmquantc( rout, g, b, arg4, arg5, dith, arg7 ); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm.m new file mode 100755 index 0000000..915e07d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm.m @@ -0,0 +1,26 @@ +function [L1,L2,phi,Txx,Txy,Tyy]=wismm(X,N); +% [L1,L2,phi,T11,T12,T22]=wismm(X,N); +% Calculate windowed image second moment matrices for image X and return +% the following values: +% +% L1 is the larger eigenvalue (lambda_1) +% L2 is the smaller eigenvalue (lambda_2) +% phi is the angle of the 1st eigenvector (phi) + +[G1,G2]=gradient(X); + +GGTxx=G1.^2; +GGTxy=G1.*G2; +GGTyy=G2.^2; + +Txx=gaussN(GGTxx,N); +Txy=gaussN(GGTxy,N); +Tyy=gaussN(GGTyy,N); + +tr=Txx+Tyy; +V1=0.5*sqrt(tr.^2-4*(Txx.*Tyy-Txy.^2)); + +L1=real(0.5*tr+V1); +L2=real(0.5*tr-V1); +phi=0.5*atan2(2*Txy,Txx-Tyy); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm2.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm2.m new file mode 100755 index 0000000..ae62ce9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm2.m @@ -0,0 +1,66 @@ +function [L1,L2,phi,aniso,pol,con,window_sizes]=wismm2(V); +% [L1,L2,phi,aniso,pol,con,window_sizes]=wismm2(V); +% Calculate windowed image second moment matrices for image V and return +% the following values: +% +% L1 is the larger eigenvalue (lambda_1) +% L2 is the smaller eigenvalue (lambda_2) +% phi is the angle of the 1st eigenvector (phi) +% + +[Gx,Gy]=gradient(V); + +GGTxx=Gx.^2; +GGTxy=Gx.*Gy; +GGTyy=Gy.^2; + +[r,c]=size(V); + +min_window_size=3; +max_window_size=3*round(min(r,c)/16); +if (-1)^max_window_size==1 + max_window_size=max_window_size+1; +end +window_step_size=2; + +window_sizes=min_window_size:2:max_window_size; +max_count=length(window_sizes); + +L1=zeros(r,c,max_count); +L2=zeros(r,c,max_count); +phi=zeros(r,c,max_count); +pol=zeros(r,c,max_count); +con=zeros(r,c,max_count); + +fprintf(1,'Integration window size: '); +counter=1; +for n=window_sizes + fprintf(1,'%d ',n); + Txx=gaussN(GGTxx,n); + Txy=gaussN(GGTxy,n); + Tyy=gaussN(GGTyy,n); + + tr=Txx+Tyy; + V1=0.5*sqrt(tr.^2-4*(Txx.*Tyy-Txy.^2)); + V1=real(V1); + + L1(:,:,counter)=0.5*tr+V1; + L2(:,:,counter)=0.5*tr-V1; + phi(:,:,counter)=0.5*atan2(2*Txy,Txx-Tyy); + + % do polarity stuff here + grad_smooth_x=gaussN(Gx,n); + grad_smooth_y=gaussN(Gy,n); + grad_smooth_mag=sqrt(grad_smooth_x.^2+grad_smooth_y.^2); + grad_mag=sqrt(Gx.^2+Gy.^2); + grad_mag_smooth=gaussN(grad_mag,n); + pol(:,:,counter)=grad_smooth_mag./(grad_mag_smooth+eps); + + % contrast calculation + con(:,:,counter)=tr; + counter=counter+1; +end +fprintf(1,'\n') + +aniso=1-L2./(L1+eps); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm3.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm3.m new file mode 100755 index 0000000..89c56ef --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/wismm3.m @@ -0,0 +1,71 @@ +function [L1,L2,phi,aniso,pol,con,window_sizes]=wismm3(V); +% [L1,L2,phi,aniso,pol,con,window_sizes]=wismm3(V); +% Calculate windowed image second moment matrices for image V and return +% the following values: +% +% L1 is the larger eigenvalue (lambda_1) +% L2 is the smaller eigenvalue (lambda_2) +% phi is the angle of the 1st eigenvector (phi) +% + +[Gx,Gy]=gradient(V); + +GGTxx=Gx.^2; +GGTxy=Gx.*Gy; +GGTyy=Gy.^2; + +[r,c]=size(V); + +min_window_size=3; +max_window_size=3*round(min(r,c)/16); +if (-1)^max_window_size==1 + max_window_size=max_window_size+1; +end +window_step_size=2; + +window_sizes=min_window_size:2:max_window_size; +max_count=length(window_sizes); + +L1=zeros(r,c,max_count); +L2=zeros(r,c,max_count); +phi=zeros(r,c,max_count); +pol=zeros(r,c,max_count); +con=zeros(r,c,max_count); + +fprintf(1,'Integration window size: '); +counter=1; +for n=window_sizes + fprintf(1,'%d ',n); + Txx=gaussN(GGTxx,n); + Txy=gaussN(GGTxy,n); + Tyy=gaussN(GGTyy,n); + + tr=Txx+Tyy; + V1=0.5*sqrt(tr.^2-4*(Txx.*Tyy-Txy.^2)); + V1=real(V1); + + L1(:,:,counter)=0.5*tr+V1; + L2(:,:,counter)=0.5*tr-V1; + phi(:,:,counter)=0.5*atan2(2*Txy,Txx-Tyy); + + % do polarity stuff here + [P,angle_vector]=polarity(Gx,Gy,n); + quant_bound=angle_vector(2)/2; + % (quantize angle and pull corresponding polarity out of P) + % (perhaps use set-theoretic functions for masking out P???) + for m=1:length(angle_vector); + a=angle_vector(end-m+1); + old_pol=pol(:,:,counter); + Pmask=abs(cos(phi(:,:,counter)-a))>=cos(quant_bound); + Pmask=Pmask&(old_pol==0); % prevent pileup on quant. boundaries + pol(:,:,counter)=old_pol+(Pmask.*P(:,:,m)); + end + + % contrast calculation + con(:,:,counter)=tr; + counter=counter+1; +end +fprintf(1,'\n') + +aniso=1-L2./(L1+eps); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_command.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_command.m new file mode 100755 index 0000000..954a39e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_command.m @@ -0,0 +1,8 @@ +function write_command(fname,fn_base,para) + +fid = fopen(fname,'w'); + +fprintf(fid,'%s ',fn_base); +fprintf(fid,'%d ',para); +fprintf(fid,'\nrun\n'); +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_test.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_test.m new file mode 100755 index 0000000..e444c27 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/write_test.m @@ -0,0 +1,38 @@ + +I_max = 250; +tex_max = 30; + +%fnames = [130038,130039,130042,130056,130057]; +%fnames = [334074 334031 334044 334003 334065 334000 334039 334018 334002 334029] + +fnames = 130057; + +for j=1:length(fnames), +fname = sprintf('images/%d.pgm',fnames(j)); + +sigs = [1/sqrt(2),1,sqrt(2),2,2*sqrt(2)];r = 3;szs = round(r*3*sigs); +szs = szs(length(szs))*ones(1,length(szs)); +num_ori = 6; + +I = readpgm(fname); +[text_des,filters] = compute_filter_fft(I,sigs,r,szs,num_ori); + +outname = sprintf('plaatje_data/%d',fnames(j)); + +cutsz =20; +I = cutoff(I,cutsz);%figure(1);im(I); +text_des = cutoff(text_des,cutsz); + +writeout_feature(I,text_des(:,:,:),outname,I_max,tex_max); + + +if 0, +for j=0:30, + cm = sprintf('!mv plaatje_data/134013.pfm_%d.pfm plaatje_data/134013_%d.pfm',j,j); + disp(cm);eval(cm); +end +end + +end + +exit diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writeout_feature.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writeout_feature.m new file mode 100755 index 0000000..5376d5f --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writeout_feature.m @@ -0,0 +1,40 @@ +function writeout_feature(I,tex,fname,I_max,tex_max) +% +% writeout_feature(I,tex,fname,I_max,tex_max) +% +% + + +%%%%% print out image +I_min = min(I(:)); + +I = I-I_min; +I = min(1,I/(I_max-I_min)); + +I = 2*I-1; + +j = 0; +fn = sprintf('%s_%d.pfm',fname,j); +cm = sprintf('writepfm: I->%s',fn); +disp(cm); +writepfm(fn,I); + + +%%% print out texture +nf = size(tex,3) + +for j=1:nf, + + fn = sprintf('%s_%d.pfm',fname,j); + cm = sprintf('writepfm:tex_%d->%s',j,fn); + disp(cm); + +tex(:,:,j) = tex(:,:,j)/tex_max;fprintf('.'); +tex(:,:,j) = tex(:,:,j).*(tex(:,:,j)<=1) + 1*(tex(:,:,j)>1);fprintf('.') +tex(:,:,j) = tex(:,:,j).*(tex(:,:,j)>=-1) + (-1)*(tex(:,:,j)<-1);fprintf('.'); + + writepfm(fn,tex(:,:,j)); + + +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepfm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepfm.m new file mode 100755 index 0000000..a831970 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepfm.m @@ -0,0 +1,11 @@ +function writepfm(name,I) +% +% writepfm(name,I) +% + [nr,nc] = size(I); + + fid = fopen(name, 'w'); + fprintf(fid, '%d %d\n', nr,nc); + fprintf(fid,'%f ',I); + fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepgm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepgm.m new file mode 100755 index 0000000..113cb18 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepgm.m @@ -0,0 +1,8 @@ +function I = writepgm(name,I) + + [y,x] = size(I); + + fid = fopen(name, 'w'); + fprintf(fid, 'P5\n%d %d\n255\n', x,y); + fwrite(fid, I', 'uint8'); + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepmm.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepmm.m new file mode 100755 index 0000000..675df93 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepmm.m @@ -0,0 +1,14 @@ +function writepmm(name,I) +% +% writepmm(name,I) +% + + [nr,nc,nb] = size(I); + + fid = fopen(name,'w'); + + fprintf(fid, 'P5\n%d %d %d\n255\n', nc,nr,nb); + + fprintf(fid,'%f ',I); + fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepnm5.m b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepnm5.m new file mode 100755 index 0000000..633fba9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filter_hist/writepnm5.m @@ -0,0 +1,26 @@ +function writepnm5(name,I) +% +% writepnm5(name,I) +% +% I is a mul-band image +% + + [nr,nc,nb] = size(I); + + fid = fopen(name,'w'); + + fprintf(fid, 'P5\n%d %d %d\n255\n', nc,nr,nb); + + n = nr*nc; + + J = []; + + for j=1:nb, + J = [J,reshape(I(:,:,j)',n,1)]; + end + + J = reshape(J',nb*n,1); + + fprintf(fid,'%f ',J); + fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog1.m b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog1.m new file mode 100755 index 0000000..dd8e87b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog1.m @@ -0,0 +1,32 @@ +function H=doog1(sig,r,th,N); +% H=doog1(sig,r,th,N); + + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +phi=pi*th/180; +sigy=sig; +sigx=r*sig; +R=[cos(phi) -sin(phi); sin(phi) cos(phi)]; +C=R*diag([sigx,sigy])*R'; + +X=[x(:) y(:)]; + +Gb=gaussian(X,[0 0]',C); +Gb=reshape(Gb,N,N); + +m=R*[0 sig]'; + +a=1; +b=-1; + +% make odd-symmetric filter +Ga=gaussian(X,m/2,C); +Ga=reshape(Ga,N,N); +Gb=rot90(Ga,2); +H=a*Ga+b*Gb; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog2.m b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog2.m new file mode 100755 index 0000000..a0511cb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/doog2.m @@ -0,0 +1,38 @@ +function G=doog2(sig,r,th,N); +% G=doog2(sig,r,th,N); +% Make difference of offset gaussians kernel +% theta is in degrees +% (see Malik & Perona, J. Opt. Soc. Amer., 1990) +% +% Example: +% >> imagesc(doog2(1,12,0,64,1)) +% >> colormap(gray) + +% by Serge Belongie + +no_pts=N; % no. of points in x,y grid + +[x,y]=meshgrid(-(N/2)+1/2:(N/2)-1/2,-(N/2)+1/2:(N/2)-1/2); + +phi=pi*th/180; +sigy=sig; +sigx=r*sig; +R=[cos(phi) -sin(phi); sin(phi) cos(phi)]; +C=R*diag([sigx,sigy])*R'; + +X=[x(:) y(:)]; + +Gb=gaussian(X,[0 0]',C); +Gb=reshape(Gb,N,N); + +m=R*[0 sig]'; +Ga=gaussian(X,m,C); +Ga=reshape(Ga,N,N); +Gc=rot90(Ga,2); + +a=-1; +b=2; +c=-1; + +G = a*Ga + b*Gb + c*Gc; + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_even2.m b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_even2.m new file mode 100755 index 0000000..f7f4527 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_even2.m @@ -0,0 +1,45 @@ +function FB = make_filterbank(num_ori,filter_scales,wsz,enlong) +% +% F = make_filterbank(num_ori,num_scale,wsz) +% + +if nargin<4, + enlong = 3; +end + +enlong = 2*enlong; + +% definine filterbank +%num_ori=6; +%num_scale=3; + +num_scale = length(filter_scales); + +M1=wsz; % size in pixels +M2=M1; + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +FB=zeros(M1,M2,num_ori,num_scale); + +% elongated filter set +counter = 1; + +for m=1:num_scale + for n=1:num_ori + % r=12 here is equivalent to Malik's r=3; + f=doog2(filter_scales(m),enlong,ori_offset+(n-1)*ori_incr,M1); + FB(:,:,n,m)=f; + end +end + +FB=reshape(FB,M1,M2,num_scale*num_ori); +total_num_filt=size(FB,3); + +for j=1:total_num_filt, + F = FB(:,:,j); + a = sum(sum(abs(F))); + FB(:,:,j) = FB(:,:,j)/a; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_odd2.m b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_odd2.m new file mode 100755 index 0000000..0059dca --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/make_filterbank_odd2.m @@ -0,0 +1,46 @@ +function FB = make_filterbank(num_ori,filter_scales,wsz,enlong) +% +% F = make_filterbank(num_ori,num_scale,wsz) +% + +if nargin<4, + enlong = 3; +end + +enlong = enlong*2; + +% definine filterbank +%num_ori=6; +%num_scale=3; + +num_scale = length(filter_scales); + +M1=wsz; % size in pixels +M2=M1; + +ori_incr=180/num_ori; +ori_offset=ori_incr/2; % helps with equalizing quantiz. error across filter set + +FB=zeros(M1,M2,num_ori,num_scale); + + +% elongated filter set +counter = 1; + +for m=1:num_scale + for n=1:num_ori + % r=12 here is equivalent to Malik's r=3; + f=doog1(filter_scales(m),enlong,ori_offset+(n-1)*ori_incr,M1); + FB(:,:,n,m)=f; + end +end + +FB=reshape(FB,M1,M2,num_scale*num_ori); +total_num_filt=size(FB,3); + +for j=1:total_num_filt, + F = FB(:,:,j); + a = sum(sum(abs(F))); + FB(:,:,j) = FB(:,:,j)/a; +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/quadedgep2.m b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/quadedgep2.m new file mode 100755 index 0000000..5041377 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/filtersQuad/quadedgep2.m @@ -0,0 +1,188 @@ +% function [xs,ys,gx,gy,par,threshold,mag,mage,g,FIe,FIo,mago] = quadedgep(I,par,threshold); +% Input: +% I = image +% par = vector for 4 parameters +% [number of filter orientations, number of scale, filter size, elongation] +% To use default values, put 0. +% threshold = threshold on edge strength +% Output: +% [x,y,gx,gy] = locations and gradients of an ordered list of edgels +% x,y could be horizontal or vertical or 45 between pixel sites +% but it is guaranteed that there [floor(y) + (floor(x)-1)*nr] +% is ordered and unique. In other words, each edgel has a unique pixel id. +% par = actual par used +% threshold = actual threshold used +% mag = edge magnitude +% mage = phase map +% g = gradient map at each pixel +% [FIe,FIo] = odd and even filter outputs +% mago = odd filter output of optimum orientation + +% Stella X. Yu, 2001 + +% This is the multi scale version of the filtering +% For the moment the parameters are defined by default. See line 34 + + +function [x,y,gx,gy,par,threshold,mag_s,mage,g,FIe,FIo,mago] = quadedgep2(I,par,data,threshold); + + +if nargin<4 | isempty(threshold), + threshold = 0.1; +end + +[r,c] = size(I); +def_par = [4,30,3]; + +display_on = 1; + +% take care of parameters, any missing value is substituted by a default value +if nargin<2 | isempty(par), + par = def_par; +end +% par(end+1:4)=0; +% par = par(:); +% j = (par>0); +% have_value = [ j, 1-j ]; +% j = 1; n_filter = have_value(j,:) * [par(j); def_par(j)]; +% j = 2; n_scale = have_value(j,:) * [par(j); def_par(j)]; +% j = 3; winsz = have_value(j,:) * [par(j); def_par(j)]; +% j = 4; enlong = have_value(j,:) * [par(j); def_par(j)]; + +n_ori = par(1); %if it doesn't work, par<-def_par + +winsz = par(2); +enlong = par(3); + +% always make filter size an odd number so that the results will not be skewed +j = winsz/2; +if not(j > fix(j) + 0.1), + winsz = winsz + 1; +end + +% filter the image with quadrature filters +if (isempty(data.W.scales)) + error ('no scales entered'); +end + +n_scale=length(data.W.scales); +filter_scales=data.W.scales; +% +% if strcmp(data.dataWpp.mode,'multiscale') +% n_scale=size((data.dataWpp.scales),2); +% filter_scales=data.dataWpp.scales; +% else +% filter_scales=data.dataWpp.scales(1); +% n_scale=1; +% end +% if n_scale>0&&strcmp(data.dataWpp.mode,'multiscale') +% if (~isempty(data.dataWpp.scales)) +% filter_scales=data.dataWpp.scales; +% else +% filter_scales=zeros(1,n_scale); +% +% for i=1:n_scale, +% filter_scales(i)=(sqrt(2))^(i-1); +% end +% data.dataWpp.scales=filter_scales; +% end +% else filter_scale=1; +% data.dataWpp.scales=filter_scales; +% end +% +% %%%%%%% juste pour que ca tourne +% if ~strcmp(data.dataWpp.mode,'multiscale') +% filter_scales=data.dataWpp.scales(1); +% n_scale=4; +% end +% %%%%%%%%%%%% + +FBo = make_filterbank_odd2(n_ori,filter_scales,winsz,enlong); +FBe = make_filterbank_even2(n_ori,filter_scales,winsz,enlong); +n = ceil(winsz/2); +f = [fliplr(I(:,2:n+1)), I, fliplr(I(:,c-n:c-1))]; +f = [flipud(f(2:n+1,:)); f; flipud(f(r-n:r-1,:))]; +FIo = fft_filt_2(f,FBo,1); +FIo = FIo(n+[1:r],n+[1:c],:); +FIe = fft_filt_2(f,FBe,1); +FIe = FIe(n+[1:r],n+[1:c],:); + +% compute the orientation energy and recover a smooth edge map +% pick up the maximum energy across scale and orientation +% even filter's output: as it is the second derivative, zero cross localize the edge +% odd filter's output: orientation + +[nr,nc,nb] = size(FIe); + +FIe = reshape(FIe, nr,nc,n_ori,length(filter_scales)); +FIo = reshape(FIo, nr,nc,n_ori,length(filter_scales)); + +mag_s = zeros(nr,nc,n_scale); +mag_a = zeros(nr,nc,n_ori); + +mage = zeros(nr,nc,n_scale); +mago = zeros(nr,nc,n_scale); +mage = zeros(nr,nc,n_scale); +mago = zeros(nr,nc,n_scale); + + + +for i = 1:n_scale, + mag_s(:,:,i) = sqrt(sum(FIo(:,:,:,i).^2,3)+sum(FIe(:,:,:,i).^2,3)); + mag_a = sqrt(FIo(:,:,:,i).^2+FIe(:,:,:,i).^2); + [tmp,max_id] = max(mag_a,[],3); + + base_size = nr * nc; + id = [1:base_size]'; + mage(:,:,i) = reshape(FIe(id+(max_id(:)-1)*base_size+(i-1)*base_size*n_ori),[nr,nc]); + mago(:,:,i) = reshape(FIo(id+(max_id(:)-1)*base_size+(i-1)*base_size*n_ori),[nr,nc]); + + mage(:,:,i) = (mage(:,:,i)>0) - (mage(:,:,i)<0); + + if display_on, + ori_incr=pi/n_ori; % to convert jshi's coords to conventional image xy + ori_offset=ori_incr/2; + theta = ori_offset+([1:n_ori]-1)*ori_incr; % orientation detectors + % [gx,gy] are image gradient in image xy coords, winner take all + + ori = theta(max_id); + ori = ori .* (mago(:,:,i)>0) + (ori + pi).*(mago(:,:,i)<0); + gy{i} = mag_s(:,:,i) .* cos(ori); + gx{i} = -mag_s(:,:,i) .* sin(ori); + g = cat(3,gx{i},gy{i}); + + % phase map: edges are where the phase changes + mag_th = max(max(mag_s(:,:,i))) * threshold; + eg = (mag_s(:,:,i)>mag_th); + h = eg & [(mage(2:r,:,i) ~= mage(1:r-1,:,i)); zeros(1,nc)]; + v = eg & [(mage(:,2:c,i) ~= mage(:,1:c-1,i)), zeros(nr,1)]; + [y{i},x{i}] = find(h | v); + k = y{i} + (x{i}-1) * nr; + h = h(k); + v = v(k); + y{i} = y{i} + h * 0.5; % i + x{i} = x{i} + v * 0.5; % j + t = h + v * nr; + gx{i} = g(k) + g(k+t); + k = k + (nr * nc); + gy{i} = g(k) + g(k+t); + +% figure(1); +% clf; +% imagesc(I);colormap(gray); +% hold on; +% quiver(x,y,gx,gy); hold off; +% title(sprintf('scale = %d, press return',i)); + + % pause; + 0; +else + x = []; + y = []; + gx = []; + gy =[]; + g= []; + end +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/convert422.m b/SD-VBS/common/toolbox/toolbox_basic/io/convert422.m new file mode 100755 index 0000000..919e82e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/convert422.m @@ -0,0 +1,27 @@ +image_current = '/hid/jshi'; + +image_dir = 'vr05_5 '; +pg_path = '/hid/jshi/422toppm/422toppm'; + +cm = sprintf('cd %s',image_dir); +disp(cm); +eval(cm); + +d = dir('seq*'); +filename = char(sort({d.name})); + +for j=1:size(filename), + cm = sprintf('!%s %s',pg_path,deblank(filename(j,:))); +disp(cm); +eval(cm); +end + + +%%% change back +cm = sprintf('cd %s',image_current); +disp(cm);eval(cm); + + +if 0, + deblank(filename(f,:)); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/im_vd.m b/SD-VBS/common/toolbox/toolbox_basic/io/im_vd.m new file mode 100755 index 0000000..590cd9b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/im_vd.m @@ -0,0 +1,6 @@ +function J = im_vd(I); + +J(:,:,1) = I(1:2:end,1:2:end); +J(:,:,2) = I(2:2:end,1:2:end); + +montage2(J); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/imread2.m b/SD-VBS/common/toolbox/toolbox_basic/io/imread2.m new file mode 100755 index 0000000..27a5e4b --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/imread2.m @@ -0,0 +1,45 @@ +function I = imread2(fname,im_dir); +% +% I = imread2(fname,im_dir); +% + +cur_dir = pwd; + +if nargin>1, + cd(im_dir); +end + +%%% put on the necessary extension +d = dir(fname); + +if isempty(d), + d = dir([fname,'*']); +end + +if isempty(d), + I = []; +else + + fname = d.name; + + %%% find extension + k = findstr(fname,'.'); + ext = fname(k(end)+1:end); + + if (ext == 'bz2'), + cm = sprintf('!bzip2 -d %s',fname); + disp(cm);eval(cm); + I = imread2(fname(1:k(end-1)-1)); + cm = sprintf('!bzip2 %s',fname(1:k(end)-1)); + disp(cm);eval(cm); + elseif (ext == 'ppm'); + I = readppm(fname); + elseif (ext == 'pgm'); + I = readpgm(fname); + else + I = imread(fname); +I = double(I)/255; + end +end + +cd(cur_dir); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/peek_pgm_size.m b/SD-VBS/common/toolbox/toolbox_basic/io/peek_pgm_size.m new file mode 100755 index 0000000..13e54cd --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/peek_pgm_size.m @@ -0,0 +1,19 @@ +function [nr,nc] = peek_pgm_size(filename) +% function [nr,nc] = peek_pgm_size(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + + +fid = fopen(filename,'r'); +fscanf(fid, 'P5\n'); +cmt = '#'; +while findstr(cmt, '#'), + cmt = fgets(fid); + if length(findstr(cmt, '#')) ~= 1, + YX = sscanf(cmt, '%d %d'); + nc = YX(1); nr = YX(2); + end +end + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/pgmread.m b/SD-VBS/common/toolbox/toolbox_basic/io/pgmread.m new file mode 100755 index 0000000..49a35a8 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/pgmread.m @@ -0,0 +1,24 @@ +function [img,header] = pgmread(filename) +% +% [img,header] = pgmread(filename) + +[fid, msg] = fopen(filename, 'r'); +if fid == -1, + error(msg) +end + +head = []; +good = 0; +while (good == 0) , + l = fgetl(fid); + if (length(l) == 3), + if (l == '255'), + good = 1; + sze = sscanf(header,'%d'); + end + end + header= l; +end + +img = fread(fid, sze', 'uchar')'; +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/ppmtojpg.m b/SD-VBS/common/toolbox/toolbox_basic/io/ppmtojpg.m new file mode 100755 index 0000000..ce47e45 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/ppmtojpg.m @@ -0,0 +1,25 @@ +function []= ppm2jpg(fname,dlm,ori) +% +% ppm2jpg(fname,dlm,ori) +% +% dlm =1, remove the file extension from fname +% before convert +% ori =1, transpose the image +% + +if dlm, + dlm = findstr(fname,'.'); + fname = fname(1:dlm(end)-1); +end + +fname_1 = sprintf('%s.ppm',fname); +I = readppm(fname_1); + +if ori == 1, + I = permute(I,[2 1 3]); +end + + +fname_2 = sprintf('%s.jpg',fname); +imwrite(I,fname_2,'jpeg','Quality',90); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read422.m b/SD-VBS/common/toolbox/toolbox_basic/io/read422.m new file mode 100755 index 0000000..31a27f9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read422.m @@ -0,0 +1,45 @@ +function I = read422(fname,nc); +% +% I = read422(fname,width); +% +% read in a .422 file, need to pass image width, default = 640 +% + +% assume image width = 640 +if nargin<2, + nc = 640; +end + +%% find the image size +fid = fopen(fname); +fseek(fid,0,1); +fsize = ftell(fid); + +nr = fsize/nc/2; +fseek(fid,0,-1); + +%% read in Ybr data +data = fread(fid,fsize,'uchar'); + +%%% extract Y, Cb, Cr +Y1 = data(1:2:end); Y1 = reshape(Y1,nc,nr)'; +Cb1 = data(2:4:end); Cb1 = reshape(Cb1,nc/2,nr)'; +Cr1 = data(4:4:end); Cr1 = reshape(Cr1,nc/2,nr)'; + +Cb = zeros(size(Y1)); +Cr = zeros(size(Y1)); + +Cb(:,1:2:end) = Cb1; Cb(:,2:2:end) = Cb1; +%Cb(:,2:2:end) = 0.5*(Cb1+[Cb1(:,2:end),Cb1(:,end)]); + +Cr(:,1:2:end) = Cr1; Cr(:,2:2:end) = Cr1; +%Cr(:,2:2:end) = 0.5*(Cr1+[Cr1(:,2:end),Cr1(:,end)]); + +%%% convert to r,g,b +r = 1.164*(Y1-16.0) + 1.596*(Cr-128.0); +g = 1.164*(Y1-16.0) - 0.813*(Cr-128.0) - 0.391*(Cb-128.0); +b = 1.164*(Y1-16.0) + 2.018*(Cb-128.0); + +I = cat(3,r,g,b); +I = max(0,min(I,255)); +I = I/255; diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read422f.m b/SD-VBS/common/toolbox/toolbox_basic/io/read422f.m new file mode 100755 index 0000000..0063000 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read422f.m @@ -0,0 +1,50 @@ +function I = read422(fname,nc); +% +% I = read422(fname,width); +% +% read in a .422 file, need to pass image width, default = 640 +% + +% assume image width = 640 +if nargin<2, + nc = 640; +end + +%% find the image size +fid = fopen(fname); +fseek(fid,0,1); +fsize = ftell(fid); + +nr = fsize/nc/2; + +fseek(fid,0,-1); + +%% read in Ybr data +data = fread(fid,fsize,'uchar'); + +%%% extract Y, Cb, Cr +Y1 = data(1:2:end); Y1 = reshape(Y1,nc,nr)'; +Cb1 = data(2:4:end); Cb1 = reshape(Cb1,nc/2,nr)'; +Cr1 = data(4:4:end); Cr1 = reshape(Cr1,nc/2,nr)'; + +Cb = zeros(size(Y1)); +Cr = zeros(size(Y1)); + +Cb(:,1:2:end) = Cb1; Cb(:,2:2:end) = Cb1; +%Cb(:,2:2:end) = 0.5*(Cb1+[Cb1(:,2:end),Cb1(:,end)]); + +Cr(:,1:2:end) = Cr1; Cr(:,2:2:end) = Cr1; +%Cr(:,2:2:end) = 0.5*(Cr1+[Cr1(:,2:end),Cr1(:,end)]); + +%%% convert to r,g,b +r = 1.164*(Y1-16.0) + 1.596*(Cr-128.0); +g = 1.164*(Y1-16.0) - 0.813*(Cr-128.0) - 0.391*(Cb-128.0); +b = 1.164*(Y1-16.0) + 2.018*(Cb-128.0); + +r = flipud(max(0,min(r,255))); +g = flipud(max(0,min(g,255))); +b = flipud(max(0,min(b,255))); + +I = cat(3,r,g,b); + +I = permute(I/255,[2,1,3]); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_cimgs.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_cimgs.m new file mode 100755 index 0000000..d5df7f5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_cimgs.m @@ -0,0 +1,40 @@ +function Is = read_imgs(homedir,imgdir,prename,postname,digits,startid,endid,step_img) +% +% Is = read_imgs(homedir,imgdir,prename,postname,digits,startid,endid,step_img) +% + + + +command = ['%s%s%s%.',num2str(digits),'d%s']; + +fname = sprintf(command,homedir,imgdir,prename,startid,postname); +disp(fname); +if (strcmp('.ppm',postname)), + I1 = readppm(fname); +else + I1 = imread(fname); +end + + +Is = zeros(size(I1,1),size(I1,2),size(I1,3),1+floor((endid-startid)/step_img)); +Is(:,:,:,1) = I1; +im_id = 1; +for j = startid+step_img:step_img:endid, + command = ['%s%s%s%.',num2str(digits),'d%s']; + fname = sprintf(command,homedir,imgdir,prename,j,postname); + disp(fname); + im_id = im_id+1; + + if (strcmp('.ppm',postname)), + Is(:,:,:,im_id) = readppm(fname); + else + a = imread(fname); + Is(:,:,:,im_id) = a; + end +end + + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm.m new file mode 100755 index 0000000..3f7b69d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm.m @@ -0,0 +1,26 @@ +function [evs,ev_info] = read_ev_pgm(basename,start_id,end_id,neigs) +% +% evs = read_ev_pgm(basename,start_id,end_id,neigs) +% +% + +fname = sprintf('%s_ev_%.2d.%.2d.pgm',basename,start_id,1) +[nr,nc] = peek_pgm_size(fname); + +evs = zeros(nr,nc,neigs-1,start_id-end_id+1); +ev_info = zeros(4,neigs-1,start_id-end_id+1); + +for j=start_id:end_id, + for k=1:neigs-1, + + fname = sprintf('%s_ev_%.2d.%.2d.pgm',basename,j,k); + [I,info] = readpgm_evinfo(fname); + + if (length(info)<4) + info = [0;0;0;0]; + end + + evs(:,:,k,j-start_id+1) = I; + ev_info(:,k,j-start_id+1) = info'; + end +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm2.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm2.m new file mode 100755 index 0000000..b0cc3f9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm2.m @@ -0,0 +1,29 @@ +function [evs,ev_info] = read_ev_pgm2(basename,start_id,end_id,neigs) +% +% evs = read_ev_pgm(basename,start_id,end_id,neigs) +% +% read_ev_pgm.m modified by SXY in Feb. 2001. +% The first eigenvector is also included + +fname = sprintf('%s_ev_%.2d.%.2d.pgm',basename,start_id,1) +[nr,nc] = peek_pgm_size(fname); + +evs = zeros(nr,nc,neigs,start_id-end_id+1); +ev_info = zeros(4,neigs,start_id-end_id+1); + +for j=start_id:end_id, + + for k=1:neigs, + + fname = sprintf('%s_ev_%.2d.%.2d.pgm',basename,j,k-1); + + [I,info] = readpgm_evinfo(fname); + + if (length(info)<4) + info = [0;0;0;0]; + end + + evs(:,:,k,j-start_id+1) = I; + ev_info(:,k,j-start_id+1) = info'; + end +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm_real.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm_real.m new file mode 100755 index 0000000..d985679 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_ev_pgm_real.m @@ -0,0 +1,30 @@ +function [evs,ev_info] = read_ev_pgm(basename,start_id,end_id,neigs) +% +% evs = read_ev_pgm(basename,start_id,end_id,neigs) +% +% + +fname = sprintf('%s_ev_%.2d.%.2d.pgm',basename,start_id,1); +[nr,nc] = peek_pgm_size(fname); + +evs = zeros(nr,nc,neigs-1,start_id-end_id+1); +ev_info = zeros(4,neigs-1,start_id-end_id+1); + +for j=start_id:end_id, + for k=1:neigs, + + fname = sprintf('%s_ev_%.2d.%.2d.pgm',basename,j,k-1); + [I,info] = readpgm_evinfo(fname); + + evs(:,:,k,j-start_id+1) = I; + ev_info(:,k,j-start_id+1) = info'; + end +end + +evs = squeeze(evs); + +for j=1:neigs, + evs(:,:,j) = (evs(:,:,j)/ev_info(3,j)) +ev_info(1,j); + %evs(:,:,j) = evs(:,:,j)/norm(reshape(evs(:,:,j),nr*nc,1)); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_imgs.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_imgs.m new file mode 100755 index 0000000..f84486c --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_imgs.m @@ -0,0 +1,47 @@ +function Is = read_imgs(homedir,imgdir,prename,postname,digits,startid,endid,step_img) +% +% Is = read_imgs(homedir,imgdir,prename,postname,digits,startid,endid,step_img) +% + + + +command = ['%s%s%s%.',num2str(digits),'d%s']; + +fname = sprintf(command,homedir,imgdir,prename,startid,postname); +disp(fname); +if (strcmp('.pgm',postname)), + I1 = readpgm(fname); +elseif (strcmp('.ppm',postname)) + a = readppm(fname); + I1 = sum(a,3); +else + a = imread(fname); a = sum(double(a),3); + I1 = a; +end + + +Is = zeros(size(I1,1),size(I1,2),1+floor((endid-startid)/step_img)); +Is(:,:,1) = I1; +im_id = 1; +for j = startid+step_img:step_img:endid, + command = ['%s%s%s%.',num2str(digits),'d%s']; + fname = sprintf(command,homedir,imgdir,prename,j,postname); + disp(fname); + im_id = im_id+1; + + if (strcmp('.pgm',postname)), + Is(:,:,im_id) = readpgm(fname); + elseif (strcmp('.ppm',postname)) + a = readppm(fname); + Is(:,:,im_id) = sum(a,3); + else + a = imread(fname); a = sum(double(a),3); + Is(:,:,im_id) = a; + end +end + + + + + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_pmm.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_pmm.m new file mode 100755 index 0000000..9e2eed1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_pmm.m @@ -0,0 +1,12 @@ +function I = read_pmm(fname) + +fid = fopen(fname,'r'); + +[A] = fscanf(fid,'%d\n',3); + +I = fscanf(fid,'%d',prod(A)); + + +I = reshape(I,A(2),A(1))'; + +I = squeeze(I); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_scan.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_scan.m new file mode 100755 index 0000000..6ad818a --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_scan.m @@ -0,0 +1,42 @@ +function [img,sizeinfo] = pgmread(filename) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + + +fname_header = sprintf('%s.h01',filename); +fname_data = sprintf('%s.i01',filename); + +fid = fopen(fname_header,'r'); + + +done = 0; +while done~=3, + cmt = fgets(fid) + if (findstr(cmt,'!matrix size[1]')), + nc = sscanf(cmt,'!matrix size[1] :=%d'); + done = done+1; + elseif (findstr(cmt,'!matrix size[2]')), + nr = sscanf(cmt,'!matrix size[2] :=%d'); + done = done+1; + elseif (findstr(cmt,'!matrix size[3]')), + ns = sscanf(cmt,'!matrix size[3] :=%d'); + done = done+1; + end +end +fclose(fid); + +fid = fopen(fname_data,'r'); + +%img = fscanf(fid,'%d',size); +%img = img'; + +img = fread(fid,nc*nr*ns,'uint8'); +img = reshape(img,nc,nr,ns); + +sizeinfo(1) = nr; +sizeinfo(2) = nc; +sizeinfo(3) = ns; + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/read_seg_file.m b/SD-VBS/common/toolbox/toolbox_basic/io/read_seg_file.m new file mode 100755 index 0000000..a056ebc --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/read_seg_file.m @@ -0,0 +1,36 @@ +function [seg_map,seg] = read_seg(filename) +% +% function seg = read_seg(filename) +% + +fid = fopen(filename,'r'); +if (fid < 0), + error(sprintf('can not find file: %s',filename)); +end + +header_done =0; +while ~header_done, + + cmt = fgets(fid); + if length(findstr(cmt,'#')) ~=1, + header_done = 1; + cmt = fgets(fid); + nc = sscanf(cmt,'width %d\n'); + cmt = fgets(fid); + nr = sscanf(cmt,'height %d\n'); + cmt = fgets(fid); + mseg = sscanf(cmt,'segments %d\n'); + cmt = fgets(fid); + end +end + +seg = fscanf(fid,'%d',100*nr); +tmp = length(seg(:))/4; +seg = reshape(seg,4,tmp)'; + +seg_map = zeros(nr,nc); + +for j=1:tmp, + seg_map(seg(j,2)+1,1+seg(j,3):1+seg(j,4)) = seg(j,1); +end + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readlines.m b/SD-VBS/common/toolbox/toolbox_basic/io/readlines.m new file mode 100755 index 0000000..90bc944 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readlines.m @@ -0,0 +1,30 @@ +function [lines,indexes] = readlines(fname) +% +% [lines,indexes] = readlines(fname) +% Read Edges points from .Ins file produced by "getlines" +% lines: a num_pointsx2 matrix of the edge points +% indexes: the braking point the lines +% + +fid = fopen(fname,'r'); + +done = 0; +lines = []; +indexes = []; + +first_line = fscanf(fid,'%s',1); + +while (~done), + num_lines = sscanf(first_line(3:length(first_line)),'%d'); + disp(num_lines); + indexes = [indexes,num_lines]; + a = fscanf(fid,'%f',[2,num_lines]); + lines = [lines;a']; + + first_line = fscanf(fid,'%s',1); + if (first_line == []), + done = 1; + end +end + + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpdm3.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpdm3.m new file mode 100755 index 0000000..c21fc48 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpdm3.m @@ -0,0 +1,16 @@ +function I = readpdm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',3) +A(3) = max(1,A(3)); + +I = fscanf(fid,'%d',[A(1)*A(2)*A(3)]); + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +I = reshape(I,A(2),A(1),A(3)); + +I = permute(I,[2,1,3]); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpdmc.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpdmc.m new file mode 100755 index 0000000..37910b9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpdmc.m @@ -0,0 +1,12 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); +I = fscanf(fid,'%d',[A(2),A(1)]); +%I = fscanf(fid,'%d',[300,1000]); +I = I'; + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpfm.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpfm.m new file mode 100755 index 0000000..48ecd78 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpfm.m @@ -0,0 +1,10 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); +I = fscanf(fid,'%f',[A(1),A(2)]); + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpfm3.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpfm3.m new file mode 100755 index 0000000..15ba959 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpfm3.m @@ -0,0 +1,17 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',3); +A(3) = max(1,A(3)); + +I = fscanf(fid,'%f',[A(1)*A(2)*A(3)]); + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +I = reshape(I,A(2),A(1),A(3)); +I = permute(I,[2,1,3]); + +I = squeeze(I); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpfmc.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpfmc.m new file mode 100755 index 0000000..2039002 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpfmc.m @@ -0,0 +1,11 @@ +function I = readpfm(filename) + +fid = fopen(filename,'r'); + +A = fscanf(fid,'%d',2); +I = fscanf(fid,'%f',[A(2),A(1)]); +I = I'; + +%I = fscanf(fid,'%f',A(2)*A(1));I = reshape(I,A(1),A(2)); + +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpgm.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpgm.m new file mode 100755 index 0000000..7aaf998 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpgm.m @@ -0,0 +1,30 @@ +function img = pgmread(filename) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% this program also corrects for the shifts in the image from pm file. + + +fid = fopen(filename,'r'); +if (fid < 0), + error(sprintf('can not find file: %s',filename)); +end + +fscanf(fid, 'P5\n'); +cmt = '#'; +while findstr(cmt, '#'), + cmt = fgets(fid); + if length(findstr(cmt, '#')) ~= 1, + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end +end + +fgets(fid); + +%img = fscanf(fid,'%d',size); +%img = img'; + +img = fread(fid,[y,x],'uint8'); +img = img'; +fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpgm_evinfo.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpgm_evinfo.m new file mode 100755 index 0000000..69f80ba --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpgm_evinfo.m @@ -0,0 +1,35 @@ +function [img,ev_info] = pgmread_evinfo(filename) +% function img = pgmread(filename) +% this is my version of pgmread for the pgm file created by XV. +% +% return the information in line # + + +fid = fopen(filename,'r'); + +if (fid <0), + error(sprintf('can not find file %s',filename)); +end + +fscanf(fid, 'P5\n'); +cmt = '#'; +while findstr(cmt, '#'), + cmt = fgets(fid); + if findstr(cmt,'#'), + ev_info = sscanf(cmt,'# minv: %f, maxv: %f, scale: %f, eigval: %f'); + end + if length(findstr(cmt, '#')) ~= 1, + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end +end + +fgets(fid); + +%img = fscanf(fid,'%d',size); +%img = img'; + +img = fread(fid,[y,x],'uint8'); +img = img'; +fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readpmm.m b/SD-VBS/common/toolbox/toolbox_basic/io/readpmm.m new file mode 100755 index 0000000..88fe907 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readpmm.m @@ -0,0 +1,22 @@ +function I=readpmm(name) +% +% I=writepmm(name) +% +% I is a mul-band image +% + fid = fopen(name,'r'); + + if (fid <0), + error(sprintf('can not find file %s',name)); + end + + a = fscanf(fid,'%d',3); + nr = a(1);nc = a(2);nb = a(3); + + + I = fscanf(fid, '%f\n', nr*nc*nb); + + I = reshape(I,nc,nr,nb)'; + I = squeeze(I); + + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/readppm.m b/SD-VBS/common/toolbox/toolbox_basic/io/readppm.m new file mode 100755 index 0000000..b9dd566 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/readppm.m @@ -0,0 +1,23 @@ +function [I,r, g, b] = readppm(name) + + fid = fopen(name, 'r'); + fscanf(fid, 'P6\n'); + cmt = '#'; + while findstr(cmt, '#'), + cmt = fgets(fid); + if length(findstr(cmt, '#')) ~= 1 + YX = sscanf(cmt, '%d %d'); + y = YX(1); x = YX(2); + end + end + fgets(fid); + packed = fread(fid,[3*y,x],'uint8')'; + r = packed(:,1:3:3*y); + g = packed(:,2:3:3*y); + b = packed(:,3:3:3*y); + fclose(fid); + + I(:,:,1) = r; + I(:,:,2) = g; + I(:,:,3) = b; + I = I/255; diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/writepgm.m b/SD-VBS/common/toolbox/toolbox_basic/io/writepgm.m new file mode 100755 index 0000000..113cb18 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/writepgm.m @@ -0,0 +1,8 @@ +function I = writepgm(name,I) + + [y,x] = size(I); + + fid = fopen(name, 'w'); + fprintf(fid, 'P5\n%d %d\n255\n', x,y); + fwrite(fid, I', 'uint8'); + fclose(fid); diff --git a/SD-VBS/common/toolbox/toolbox_basic/io/writeppm.m b/SD-VBS/common/toolbox/toolbox_basic/io/writeppm.m new file mode 100755 index 0000000..3d2fed1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/io/writeppm.m @@ -0,0 +1,14 @@ +function writeppm(name,I) + + [y,x,nb] = size(I); + + fid = fopen(name, 'w'); + fprintf(fid, 'P6\n%d %d\n255\n', x,y); + + I1 = reshape(I(:,:,1)',1,x*y); + I2 = reshape(I(:,:,2)',1,x*y); + I3 = reshape(I(:,:,3)',1,x*y); + + fwrite(fid, [I1;I2;I3], 'uint8'); + fclose(fid); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/Contents.m b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/Contents.m new file mode 100755 index 0000000..3ddb232 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/Contents.m @@ -0,0 +1,26 @@ +%Functions related to the assignment problem. +%Version 1.0, 25-May-1999. +% +%Copyright (c) 1995-1999 Niclas Borlin, Dept. of Computing Science, +% Umea University, SE-901 87 UMEA, Sweden. +% Niclas.Borlin@cs.umu.se. +% www.cs.umu.se/~niclas. +% +%All standard disclaimers apply. +% +%You are free to use this code as you wish. If you use it for a +%publication or in a commercial package, please include an +%acknowledgement and/or at least send me an email. (Looks good in my CV :-). +% +%Main functions: +% hungarian - calculate a solution of the square assignment +% problem. See HELP for a reference. +% condass - calculate a condition number of the solution to the +% assignment problem. See HELP for a reference. +% allcosts - calculate the costs of all possible assignments. +% allperms - calculate all possible permutations/assignments of a +% given problem size. +% +%Test/demo functions. +% demo - short demonstration of the main functions. +% test - test/verification of the main functions. diff --git a/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allcosts.m b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allcosts.m new file mode 100755 index 0000000..ffdb8b9 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allcosts.m @@ -0,0 +1,17 @@ +function [c,p]=allcosts(C) +%ALLCOSTS Calculate all costs for an assignment problem. +% +%[c,p]=allcosts(C) +%c returns the costs, p the corresponding permutations. + +% v1.0 95-07-18. Niclas Borlin, niclas@cs.umu.se. + +p=allperm(size(C,1)); + +c=zeros(size(p,1),1); + +I=eye(size(C,1)); + +for i=1:size(p,1) + c(i)=sum(C(logical(sparse(p(i,:),1:size(C,1),1)))); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allperm.m b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allperm.m new file mode 100755 index 0000000..b8d419e --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/allperm.m @@ -0,0 +1,17 @@ +function p=allperm(n) +%ALLPERM All permutation matrix. +% +%p=allperm(n) +%Returns a matrix with all permutations of 1:n stored row-wise. + +% v1.0 95-07-18. Niclas Borlin, niclas@cs.umu.se. + +if (n<=1) + p=1; +else + q=allperm(n-1); + p=[]; + for i=1:n + p=[p;i*ones(size(q,1),1) q+(q>=i)]; + end +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/condass.m b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/condass.m new file mode 100755 index 0000000..82552e7 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/matching/pub/contrib/v5/optim/assignprob/condass.m @@ -0,0 +1,54 @@ +function [k,C1,T1,C2,T2]=condass(A) +%CONDASS Calculate condition number of the assigment problem. +% +%[k,C1,T1,C2,T2]=condass(A) +%A - A square cost matrix. +%k - The condition number of the assigment problem. +%C1 - The best assigment. +%T1 - The lowest cost. +%C2 - The second best assignment. +%T2 - The second lowest cost. +% +%The condition number is calculated as the relative difference between +%the best and second best solutions, as described in Nystrom, Soderkvist, +%and Wedin, "A Note on some Identification Problems Arising in Roentgen +%Stereo Photogrammetric Analysis", J of Biomechanics, 27(10):1291-1294, +%1994. + +% v1.0 96-09-14. Niclas Borlin, niclas@cs.umu.se. + +% A substantial effort was put into this code. If you use it for a +% publication or otherwise, please include an acknowledgement and notify +% me by email. /Niclas + +% Create a large number used to block selected assignments. +big=sum(sum(A))+1; + +% Get best assigment. +[C1,T1]=hungarian(A); + +% Initialize second best solution. +T2=inf; +C2=zeros(size(C1)); + +% Create a work matrix. +B=A; +for i=1:length(C1) + % Block assigment in column i. + B(C1(i),i)=big; + % Get best assigment with this one blocked. + [C,T]=hungarian(B); + if (T=max).*(theta-pi) +... + ((theta>min)&(theta1) { + mexErrMsgTxt("Too many output arguments."); + } + if (!(mxIsSparse(in[0]))) { + mexErrMsgTxt("Input argument #1 must be of type sparse"); + } + if ( mxIsSparse(in[1]) || mxIsSparse(in[2]) ) { + mexErrMsgTxt("Input argument #2 & #3 must be of type full"); + } + + /* computation starts */ + m = mxGetM(in[0]); + n = mxGetN(in[0]); + pr = mxGetPr(in[0]); + pi = mxGetPi(in[0]); + pir = mxGetIr(in[0]); + pjc = mxGetJc(in[0]); + + i = mxGetM(in[1]); + j = mxGetN(in[1]); + xm = ((i>j)? i: j); + + i = mxGetM(in[2]); + j = mxGetN(in[2]); + yn = ((i>j)? i: j); + + if ( xm>0 && xm != m) { + mexErrMsgTxt("Row multiplication dimension mismatch."); + } + if ( yn>0 && yn != n) { + mexErrMsgTxt("Column multiplication dimension mismatch."); + } + + + nzmax = mxGetNzmax(in[0]); + cmplx = (pi==NULL ? 0 : 1); + out[0] = mxCreateSparse(m,n,nzmax,cmplx); + if (out[0]==NULL) { + mexErrMsgTxt("Not enough space for the output matrix."); + } + + qr = mxGetPr(out[0]); + qi = mxGetPi(out[0]); + qir = mxGetIr(out[0]); + qjc = mxGetJc(out[0]); + + /* left multiplication */ + x = mxGetPr(in[1]); + if (yn==0) { + for (j=0; j1); +end + +if nargin<5 | isempty(no_rep), + no_rep = 1; +end + +if visimg, + nr = nr * nc; +else + nv = nc; +end + +if nargin<6 | isempty(pixel_loc), + pixel_loc = 1:nr; +end + +% D^(1/2) +d = 1./(d(:)+eps); + +% first recover the first eigenvector +if no_rep, + u = (1/norm(d)) + zeros(nr,1); + s = [1;s(:)]; + nv = nv + 1; +else + u = []; +end + +% the full set of generalized eigenvectors +v = [u, reshape(v,[nr,nv-no_rep])]; + +% This is the real D, row sum +d = d.^2; + +% an equivalent way to compute v = diag(d) * v; +v = v .* d(:,ones(nv,1)); % to avoid using a big matrix diag(d) + +% synthesis +a = v(pixel_loc,:)*diag(s)*v'; diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/dispimg.m b/SD-VBS/common/toolbox/toolbox_basic/stella/dispimg.m new file mode 100755 index 0000000..4e419a0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/dispimg.m @@ -0,0 +1,65 @@ +% function dispimg(g,fmt,lgd,cmap) display multiple images in one figure. +% Input: +% g = a cell and fmt is a 1x2 vector specifying the layout. +% lgd = a string cell for the title of each image. +% cmap = the colormap (default is the gray, -1 for the inverted gray). +% ishori = a vector of 1/0 to display real and imag parts horizontally / vertically + +% Stella X. Yu, 2000. + +function dispimg(g,fmt,lgd,cmap,ishori); + +cellg = iscell(g); +if cellg, + num_fig = length(g); +else + num_fig = size(g,3); +end; + +if nargin<2 | isempty(fmt), + m = ceil(sqrt(num_fig)); + n = ceil(num_fig / m); +else + m = fmt(1); + n = fmt(2); +end + +if nargin<3 | isempty(lgd), + lgd = 1:num_fig; +end +if isnumeric(lgd), + lgd = cellstr(num2str(lgd(:),3)); +end +i = size(lgd); +if i(1)==1, + lgd = [lgd, cell(1,num_fig-i(2))]; +else + lgd = [lgd; cell(num_fig-i(1),1)]; +end + +if nargin<5 | isempty(ishori), + ishori = ones(num_fig,1); +end +ishori(end+1:num_fig) = ishori(end); + +for k=1:num_fig, + subplot(m,n,k); + if cellg, + showim(g{k},[],ishori(k)); + else + showim(g(:,:,k),[],ishori(k)); + end + title(lgd{k}); +end + +if nargin<4 | isempty(cmap), + cmap = gray; +end +if length(cmap)==1, + if cmap==1, + cmap = gray; + else + cmap = flipud(gray); + end +end +colormap(cmap); diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/firstncut.m b/SD-VBS/common/toolbox/toolbox_basic/stella/firstncut.m new file mode 100755 index 0000000..a22077d --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/firstncut.m @@ -0,0 +1,67 @@ +% function [v,s,d] = firstncut(base_name,rec_num) +% Input: +% base_name = image name +% rec_num = parameter record number +% Output: +% v = eigenvectors +% s = eigenvalues +% d = normalization matrix d = 1/sqrt(rowsum(abs(a))) +% Convert Jianbo Shi's Ncut Ccode results from images to matlab matrices. + +% Stella X. Yu, 2000. + +function [v,s,d] = firstncut(base_name,rec_num); + +if nargin<2 | isempty(rec_num), + rec_num = 1; +end + +cur_dir = pwd; +globalenvar; +cd(IMAGE_DIR); +cd(base_name); + feval([base_name,'_par']); + j = length(p); + if rec_num>j, + disp(sprintf('parameter record number %d out of range %d, check %s!',rec_num,j,[base_name,'_par.m'])); + Qlabel = []; + v = []; + s = []; + ev_info = []; + return; + end + nv = p(rec_num).num_eigvecs; + no_rep = (p(rec_num).offset<1e-6); + + % read the image + cm=sprintf('I = readppm(''%s.ppm'');',base_name); + eval(cm); + + % read eigenvectors + base_name_hist = sprintf('%s_%d_IC',base_name,rec_num); + if no_rep, + [v,ev_info] = read_ev_pgm(base_name_hist,1,1,nv); + else + [v,ev_info] = read_ev_pgm2(base_name_hist,1,1,nv); + end + s = ev_info(4,:)'; + + % read the normalization matrix + d = readpfmc(sprintf('%s_%d_D_IC.pfm',base_name,rec_num)); +cd(cur_dir); + +% D^(1/2) +dd = (1./(d(:)+eps)); + +% recover real eigenvectors +for j = 1:nv-no_rep, + vmin = ev_info(1,j); + vmax = ev_info(2,j); + y = v(:,:,j).*((vmax - vmin)/256) + vmin; + %validity check: x = D^(1/2)y should be normalized + x = norm(y(:).*dd); + v(:,:,j) = y./x; +end + +dispimg(cat(3,mean(I,3),v),[],[{'image'};cellstr(num2str(s,3))]); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/getfnames.m b/SD-VBS/common/toolbox/toolbox_basic/stella/getfnames.m new file mode 100755 index 0000000..4990451 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/getfnames.m @@ -0,0 +1,47 @@ +% function [fn,dn] = getfnames(direc,opt) +% Input: +% direc = directory +% opt = wildcat +% Output: +% fn = a cell with all filenames under direc and with opt +% dn = a cell with all directory names under direc and with opt +% For example, getfnames('19990910','*.jpg'); +% Set IS_PC according to your platform in globalenvar.m + +% Stella X. Yu, 2000. + +function [fn,dn] = getfnames(direc,opt) + +if (nargin<1 | isempty(direc)), + direc = '.'; +end + +if nargin<2 | isempty(opt), + opt = []; +end + +fn = {}; +dn = {}; + +cur_dir = pwd; +cd(direc); +s = dir(opt); +if isempty(s), + disp('getfnames: no data'); + return; +end + +n = length(s); +i = 1; +j = 1; +for k=1:n, + if s(k).isdir, + dn{j,1} = s(k).name; + j = j + 1; + else + fn{i,1} = s(k).name; + i = i + 1; + end +end + cd(cur_dir) +%[fn{1:n,1}]=deal(s.name); diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/getimage2.m b/SD-VBS/common/toolbox/toolbox_basic/stella/getimage2.m new file mode 100755 index 0000000..945ddd2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/getimage2.m @@ -0,0 +1,46 @@ +% function f = getimage2(imagefile) returns a normalized intensity image. +% If the file postfix is not given, then I will search any possible image file +% under the IMAGE_DIR. + +% Stella X. Yu, March 1999 + +function f = getimage2(imagefile) + +if exist(imagefile)==2, + g = {imagefile}; +else + g = {}; +end +globalenvar; +g = [g; getfnames(IMAGE_DIR,[imagefile,'.*'])]; + +j = 1; +for i=1:length(g), + k = findstr(g{i},'.'); + gp = g{i}(k(end)+1:end); + if strcmp(gp,'ppm'), + f = double(readppm(g{i})); + j = 0; + elseif sum(strcmp(gp,{'jpg','tif','jpeg','tiff','bmp','png','hdf','pcx','xwd'}))>0, + f = double(imread(g{i})); + j = 0; + end + if j==0, + disp(sprintf('This is an image read from %s under %s',g{i},IMAGE_DIR)); + break; + end +end +if j, + f = []; + disp('Image not found'); + return; +end + +if size(f,3)>1, + %f = sum(f,3)./3; + f = rgb2ntsc(f); + f = f(:,:,1); +end +minf = min(f(:)); +maxf = max(f(:)); +f = (f - minf) ./ (maxf - minf); diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/globalenvar.m b/SD-VBS/common/toolbox/toolbox_basic/stella/globalenvar.m new file mode 100755 index 0000000..1e61b61 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/globalenvar.m @@ -0,0 +1,6 @@ +% globalenvar + +HOME_DIR = '/hid/jshi/Research/walking/stella'; +IMAGE_DIR = '/hid/jshi/test_images'; +C_DIR = '/hid/jshi/Research/Ncut_code_C'; +IS_PC = 0; diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/jshincut.m b/SD-VBS/common/toolbox/toolbox_basic/stella/jshincut.m new file mode 100755 index 0000000..d0f11cb --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/jshincut.m @@ -0,0 +1,94 @@ +% function [par, rec_num] = jshincut(par,image_dir) +% Input: +% par = a structure with parameters for command_ncut.tex +% image_dir = the directory where the imagefile is stored +% Output: +% par = parameters used +% rec_num = record number in the NCut database +% Jianbo Shi's ncut_IC is applied to the image +% (If there is no .ppm format for the named image, +% conversion from related files would be attempted.) +% Results are organized according to the parameters. +% Example: jshincut('240018s'); +% See also: jshincutdefpar; ncutcheckin +% Set IS_PC according to your platform in globalenvar.m + +% Stella X. Yu, 2000. + +function [par,rec_num] = jshincut(par,image_dir) + +rec = jshincutdefpar; + +fields = fieldnames(rec); +nf = length(fields); + +if ischar(par), + imagename = par; + par = rec; + par.fname_base = imagename; +end + +globalenvar; + +if nargin<2 | isempty(image_dir), + image_dir = IMAGE_DIR; +end + +imagename = getfield(par,fields{1}); +for i=2:nf, + t = getfield(par,fields{i}); + if isempty(t), + par = setfield(par,fields{i},getfield(rec,fields{i})); + end +end + +% dir and filename +catchar = {'/','\'}; +catchar = catchar{IS_PC+1}; + +% first check if there is a ppm file for this image +if not(exist([image_dir,catchar,imagename,'.ppm'])), + j = getfnames(image_dir,[imagename,'.*']); + if isempty(j), + disp('Image not found.'); + return; + end + k = 0; + for i=1:length(j), + k = k + not(isempty(im2ppm(j{i},image_dir))); + if k==1, + disp(sprintf('%s -> %s.ppm succeeded.',j{i},imagename)); + break; + end + end + if k==0, + disp('Sorry. Attempt to convert your named image into ppm format failed.'); + return; + end +end + +cd(C_DIR); + +% generate command_ncut.tex file +fn = 'command_ncut.tex'; +fid = fopen(fn,'w'); +fprintf(fid,'%21s\t%s%c%s\n',fields{1},image_dir,catchar,imagename); +for i=2:nf, + t = getfield(par,fields{i}); + if isnumeric(t), + t = num2str(t); + end + fprintf(fid,['%21s\t%s\n'],fields{i},t); +end +fclose(fid); +%disp('You can check and modify command_ncut.tex before I run ncut_IC on it. Good?');pause(1); + +% run ncut_IC +unix(['.',catchar,'ncut_IC']); +cd(HOME_DIR); + +% check in +copyfile([C_DIR,catchar,fn],[image_dir,catchar,fn]); +rec_num = ncutcheckin(fn,image_dir,image_dir); +%delete([image_dir,catchar,imagename,'.ppm']); +%delete([image_dir,catchar,fn]); diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/jshincutdefpar.m b/SD-VBS/common/toolbox/toolbox_basic/stella/jshincutdefpar.m new file mode 100755 index 0000000..4f07192 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/jshincutdefpar.m @@ -0,0 +1,20 @@ +% function rec = jshincutdefpar; +% default parameter setting for Jianbo Shi's NCut C codes + +% Stella X. Yu, 2000. + +function rec = jshincutdefpar; + +rec.fname_base = '240018s'; +rec.fname_ext = 'ppm'; +rec.num_eigvecs = 15; +rec.spatial_neighborhood_x=20; +rec.sigma_x= 10; +rec.sig_I= -0.16; +rec.sig_IC= 0.01; +rec.hr= 2; +rec.eig_blk_sze= 3; +rec.power_D= 1; +rec.offset = 0.0; +rec.sig_filter = 1.0; +rec.elong_filter = 3.0; diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/ncutcheckin.m b/SD-VBS/common/toolbox/toolbox_basic/stella/ncutcheckin.m new file mode 100755 index 0000000..cd82ee5 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/ncutcheckin.m @@ -0,0 +1,136 @@ +% function rec_num = ncutcheckin(fn,sdir,tdir) +% Input: +% fn = parameter file name, default = 'command_ncut.tex' +% sdir = source dir for fn as well as data files +% tdir = target dir to check in, both default = IMAGE_DIR +% Output: +% rec_num = the number of current parameter records +% The imagefile_par.m is updated if fn contains a new +% parameter set. Data files are tagged and copied over to +% a subdir under tdir. +% Example: ncutcheckin; +% Set IS_PC, IMAGE_DIR according to your platform in globalenvar.m + +% Stella X. Yu, 2000. + +function rec_num = ncutcheckin(fn,sdir,tdir) + +globalenvar; + +cur_dir = pwd; + +if nargin<1 | isempty(fn), + fn = 'command_ncut.tex'; +end + +if nargin<2 | isempty(sdir), + sdir = IMAGE_DIR; +end + +if nargin<3 | isempty(tdir), + tdir = IMAGE_DIR; +end + +rec = jshincutdefpar; + +% first, generate a parameter record from fn +cd(sdir); +[names,values] = textread(fn,'%s %s','commentstyle','shell'); +n = length(names); +s = rec; +for i=1:n, + j = str2num(values{i}); + if isempty(j), + s = setfield(s,names{i},values{i}); + else + s = setfield(s,names{i},j); + end +end +cd(cur_dir); + +% special care to extract the image file name +imagename = getfield(s,names{1}); +catchar = {'/','\'}; +catchar = catchar{IS_PC + 1}; +k = max([0,findstr(imagename,catchar)]); +imagename = imagename(k+1:end); +s = setfield(s,names{1},imagename); + +% second, check if the target dir contains a profile for the image +cd(tdir); +if not(exist(imagename,'dir')), + mkdir(imagename); + cd(cur_dir); + j = [catchar,imagename,'.',getfield(s,names{2})]; + copyfile([sdir,j],[tdir,catchar,imagename,j]); + cd(tdir); +end +cd(imagename); +j = [imagename,'_par']; +if not(exist(j)), + rec_num = 1; + p = s; +else + % load par file + feval(j); + rec_num = length(p); + i = 1; + while (i<=rec_num), + k = 0; + for j=1:n, + k = k + sum(getfield(s,names{j})-getfield(p(i),names{j})); + end + if k==0, + if not(isempty(input(['Data already existed as record # ',num2str(i),... + '\nPress any non-return key to Overwrite'],'s'))), + break; + else + rec_num = i; % have checked in already, no update + cd(cur_dir); + return; + end + else + i = i + 1; + end + end + rec_num = i; % new parameter setting + p(rec_num)=s; +end +tdir = [tdir,catchar,imagename]; +cd(cur_dir); + +% third, check in data files +% leave .ppm and _edgecon, _phase files +% if not(exist([tdir,catchar,imagename,'.ppm'])), +% copyfile([sdir,catchar,imagename,'.ppm'],[tdir,catchar,imagename,'.ppm']); +% end + +% IC files only +dn = getfnames(sdir,[imagename,'*_IC*.*']); +header = sprintf('%s%c%s_%d_',tdir,catchar,imagename,rec_num); +j = length(imagename)+2; +k = length(dn); +for i=1:k, + copyfile([sdir,catchar,dn{i}],[header,dn{i}(j:end)]); + delete([sdir,catchar,dn{i}]); +end +disp(sprintf('%d files checked in as record #%d',k,rec_num)); + + +% finally, update parameter file +cd(tdir); +fid = fopen([imagename,'_par.m'],'w'); +fprintf(fid,'%% Last checked in at %s\n\n',datestr(now)); +for i=1:rec_num, + for j=1:n, + k = getfield(p(i),names{j}); + if ischar(k), + fprintf(fid,'p(%d).%s=\''%s\'';\n',i,names{j},k); + else + fprintf(fid,'p(%d).%s=%s;\n',i,names{j},num2str(k)); + end + end + fprintf(fid,'\n'); +end +fclose(fid); +cd(cur_dir); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/openfigure.m b/SD-VBS/common/toolbox/toolbox_basic/stella/openfigure.m new file mode 100755 index 0000000..e677014 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/openfigure.m @@ -0,0 +1,52 @@ +% function openfigure(m,n,caption,isnew) +function h = openfigure(m,n,caption,isnew) + +if nargin<3, + caption = ' '; +end + +if nargin<4, + isnew = 1; +end + +if (m<=0 | n<=0) + return; +end + +if isnew, + h = figure; colormap(gray); +else + h = gcf; +end +clf + +subplot('position',[0,0,0.1,0.1]); axis off; +text(0.1,0.15,sprintf('S. X. Yu, %s',date),'FontSize',8); + +subplot('position',[0,0.96,0.1,0.1]); axis off; +text(0.1,0.15,caption,'FontSize',8); + +subplot(m,n,1); +%return + +if (m==1 & n==1), + return; +end + +%set(gcf,'PaperPosition',[0.25, 8, 8,2.5*m]); +% set(gcf,'PaperPosition',[0.25,0.25,8,10.5]); +%return + +if (m<=n), + set(gcf,'PaperOrientation','landscape','PaperPosition',[0.25,0.25,10.5,8]); +else + set(gcf,'PaperPosition',[0.25,0.25,8,10.5]); +end + +% comment on PaperPosition +% [a,b,c,d] +% (a,b) is the coordinate of the lower-left corner of the figure +% (a,b) = (0,0) is the lower-left corner of the paper +% (c,d) is the coordinate of the upper-right corner of the figure relative to the lower-left corner of the figure +% Therefore, c>=a, d>=b +% Full paper position would be [0,0,8.5,11] in inches diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/showim.m b/SD-VBS/common/toolbox/toolbox_basic/stella/showim.m new file mode 100755 index 0000000..10db297 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/showim.m @@ -0,0 +1,36 @@ +% function showim(f,cmap) display real or complex image. +% When it is complex, the real part and imaginary part +% are displayed as [real,imag] in one image. +% cmap is the colormap. default = gray, -1 = inverted gray. + +% Stella X. Yu, 2000. + +function showim(f,cmap,ishori) + +if not(isreal(f)), + i = [real(f(:)); imag(f(:))]; + j = [min(i), max(i)]; + [nr,nc] = size(f); + if nargin<3 | isempty(ishori), + ishori = nr>nc; + end + if ishori, + i = zeros(nr,1); + f = [real(f), [i+j(1),i+j(2)], imag(f)]; + else + i = zeros(1,nc); + f = [real(f); [i+j(1);i+j(2)]; imag(f)]; + end +end +imagesc(f); axis off; axis image; + +if nargin<2 | isempty(cmap), + return; +end + +if cmap==1, + cmap = gray; +elseif cmap==-1, + cmap = flipud(gray); +end +colormap(cmap); \ No newline at end of file diff --git a/SD-VBS/common/toolbox/toolbox_basic/stella/showncut.m b/SD-VBS/common/toolbox/toolbox_basic/stella/showncut.m new file mode 100755 index 0000000..b1fe1f4 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/stella/showncut.m @@ -0,0 +1,92 @@ +% function [g,lgd,v,s,dd] = showncut(fn,rec_num) +% Input: +% fn = file / image name +% rec_num = Ncut record number +% Output: +% g = a cell contains 1D, 2D and 3D embeddings +% lgd = legend for g +% v = eigenvectors +% s = eigenvalues +% dd = normalization matrix = 1/sqrt(rowsum(abs(a))) +% an image is displayed + +function [g,lgd,v,s,dd] = showncut(fn,rec_num) + +globalenvar; cd(IMAGE_DIR);cd(fn); feval([fn,'_par']);cd(HOME_DIR); +par = p(rec_num); +no_rep = (par.offset<1e-6); + +[v,s,dd] = firstncut(fn,rec_num); +[m,n,nc] = size(v); + +% generate images for display +nr = 5; +num_plots = nc * nr; +g = cell(num_plots,1); +lgd = g; +names = {'r','\theta','\phi'}; +x = cell(3,1); +for j=1:nc, + g{j} = v(:,:,j); + lgd{j} = sprintf('%s_{%d} = %1.2f','\lambda', j+no_rep, s(j)); + + if j0) & y>0; + % showmask(f,mask); +end diff --git a/SD-VBS/common/toolbox/toolbox_basic/tars/TOOLBOX_calib.tar b/SD-VBS/common/toolbox/toolbox_basic/tars/TOOLBOX_calib.tar new file mode 100755 index 0000000..bb418a5 Binary files /dev/null and b/SD-VBS/common/toolbox/toolbox_basic/tars/TOOLBOX_calib.tar differ diff --git a/SD-VBS/common/toolbox/toolbox_basic/textons/dist2.m b/SD-VBS/common/toolbox/toolbox_basic/textons/dist2.m new file mode 100755 index 0000000..f2d93e1 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/textons/dist2.m @@ -0,0 +1,27 @@ +function n2 = dist2(x, c) +%DIST2 Calculates squared distance between two sets of points. +% +% Description +% D = DIST2(X, C) takes two matrices of vectors and calculates the +% squared Euclidean distance between them. Both matrices must be of +% the same column dimension. If X has M rows and N columns, and C has +% L rows and N columns, then the result has M rows and L columns. The +% I, Jth entry is the squared distance from the Ith row of X to the +% Jth row of C. +% +% See also +% GMMACTIV, KMEANS, RBFFWD +% + +% Copyright (c) Christopher M Bishop, Ian T Nabney (1996, 1997) + +[ndata, dimx] = size(x); +[ncentres, dimc] = size(c); +if dimx ~= dimc + error('Data dimension does not match dimension of centres') +end + +n2 = (ones(ncentres, 1) * sum((x.^2)', 1))' + ... + ones(ndata, 1) * sum((c.^2)',1) - ... + 2.*(x*(c')); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/textons/find_textons.m b/SD-VBS/common/toolbox/toolbox_basic/textons/find_textons.m new file mode 100755 index 0000000..e7fa4b0 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/textons/find_textons.m @@ -0,0 +1,46 @@ +function [centers,label,post,d2]=find_textons(FIw,ncenters,centers_in,n_iter); +% [centers,label,post,d2]=find_textons(FIw,ncenters,centers_in,n_iter); +% +% find textons using kmeans for windowed portion FIw of filtered image +% +% to start with centers pulled randomly from image, set centers_in=[] + +% define number of textons +%ncenters=25; + +[N1,N2,N3]=size(FIw); +% reshape filtered image stack into a long array of feature vectors +fv=reshape(FIw,N1*N2,N3); +% (each row is a feature vector) + +%centers=.01^2*randn(ncenters,N3); +% take centers randomly from within image +if isempty(centers_in) + rndnum=1+floor(N1*N2*rand(1,ncenters)); + centers_in=fv(rndnum,:); +end + +options = foptions; +options(1)=1; % Prints out error values. +options(5) = 0; +if nargin<4 + n_iter=15; +end +options(14) = n_iter; % Number of iterations. + +[centers,options,d2,post]=kmeans2(centers_in,fv,options); + +% reshuffle the centers so that the one closest to the origin +% (featureless) comes last +norms=sum(centers.^2,2); +[sortval,sortind]=sort(-norms); +centers=centers(sortind,:); +d2=reshape(d2,N1,N2,ncenters); +post=reshape(post,N1,N2,ncenters); +d2=d2(:,:,sortind); +post=post(:,:,sortind); + + +% retrieve cluster number assigned to each feature vector +[minval,label]=min(d2,[],3); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/textons/find_textons1.m b/SD-VBS/common/toolbox/toolbox_basic/textons/find_textons1.m new file mode 100755 index 0000000..b192015 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/textons/find_textons1.m @@ -0,0 +1,37 @@ +function [centers,label,post,d2]=find_textons(fv,ncenters,centers_in,n_iter); +% [centers,label,post,d2]=find_textons(FIw,ncenters,centers_in,n_iter); +% +% find textons using kmeans for windowed portion FIw of filtered image +% +% to start with centers pulled randomly from image, set centers_in=[] + +[N1,N2] =size(fv); + +% take centers randomly from within image +if isempty(centers_in) + rndnum=1+floor(N1*rand(1,ncenters)); + centers_in=fv(rndnum,:); +end + +options = foptions; +options(1)=1; % Prints out error values. +options(5) = 0; +if nargin<4 + n_iter=15; +end +options(14) = n_iter; % Number of iterations. + +[centers,options,d2,post]=kmeans2(centers_in,fv,options); + + +% retrieve cluster number assigned to each feature vector +[minval,label]=min(d2,[],2); + + +h = hist(label(:),[1:max(label(:))]); +a = h>0; +a = cumsum(a); + +[nr,nc] = size(label); +label = reshape(a(label(:)),nr,nc); + diff --git a/SD-VBS/common/toolbox/toolbox_basic/textons/kmeans2.m b/SD-VBS/common/toolbox/toolbox_basic/textons/kmeans2.m new file mode 100755 index 0000000..0bd87b2 --- /dev/null +++ b/SD-VBS/common/toolbox/toolbox_basic/textons/kmeans2.m @@ -0,0 +1,127 @@ +function [centres, options, d2, post, errlog] = kmeans2(centres, data, options) +%KMEANS Trains a k means cluster model. +% +% Description +% CENTRES = KMEANS(CENTRES, DATA, OPTIONS) uses the batch K-means +% algorithm to set the centres of a cluster model. The matrix DATA +% represents the data which is being clustered, with each row +% corresponding to a vector. The sum of squares error function is used. +% The point at which a local minimum is achieved is returned as +% CENTRES. The error value at that point is returned in OPTIONS(8). +% +% [CENTRES, OPTIONS, POST, ERRLOG] = KMEANS(CENTRES, DATA, OPTIONS) +% also returns the cluster number (in a one-of-N encoding) for each +% data point in POST and a log of the error values after each cycle in +% ERRLOG. The optional parameters have the following +% interpretations. +% +% OPTIONS(1) is set to 1 to display error values; also logs error +% values in the return argument ERRLOG. If OPTIONS(1) is set to 0, then +% only warning messages are displayed. If OPTIONS(1) is -1, then +% nothing is displayed. +% +% OPTIONS(2) is a measure of the absolute precision required for the +% value of CENTRES at the solution. If the absolute difference between +% the values of CENTRES between two successive steps is less than +% OPTIONS(2), then this condition is satisfied. +% +% OPTIONS(3) is a measure of the precision required of the error +% function at the solution. If the absolute difference between the +% error functions between two successive steps is less than OPTIONS(3), +% then this condition is satisfied. Both this and the previous +% condition must be satisfied for termination. +% +% OPTIONS(14) is the maximum number of iterations; default 100. +% +% See also +% GMMINIT, GMMEM +% + +% Copyright (c) Christopher M Bishop, Ian T Nabney (1996, 1997) + +[ndata, data_dim] = size(data); +[ncentres, dim] = size(centres); + +if dim ~= data_dim + error('Data dimension does not match dimension of centres') +end + +if (ncentres > ndata) + error('More centres than data') +end + +% Sort out the options +if (options(14)) + niters = options(14); +else + niters = 100; +end + +store = 0; +if (nargout > 3) + store = 1; + errlog = zeros(1, niters); +end + +% Check if centres and posteriors need to be initialised from data +if (options(5) == 1) + % Do the initialisation + perm = randperm(ndata); + perm = perm(1:ncentres); + + % Assign first ncentres (permuted) data points as centres + centres = data(perm, :); +end +% Matrix to make unit vectors easy to construct +id = eye(ncentres); + +% Main loop of algorithm +for n = 1:niters + + % Save old centres to check for termination + old_centres = centres; + + % Calculate posteriors based on existing centres + d2 = dist2(data, centres); + % Assign each point to nearest centre + [minvals, index] = min(d2', [], 1); + post = id(index,:); + + num_points = sum(post, 1); + % Adjust the centres based on new posteriors + for j = 1:ncentres + if (num_points(j) > 0) + centres(j,:) = sum(data(find(post(:,j)),:), 1)/num_points(j); + end + end + + % Error value is total squared distance from cluster centres + e = sum(minvals); + tmp = sort(minvals); + e95 = sqrt(tmp(round(length(tmp) * 0.95))); + erms = sqrt(e/ndata); + if store + errlog(n) = e; + end + if options(1) > 0 + fprintf(1, ' Cycle %4d RMS Error %11.6f 95-tier Error %11.6f\n', n, erms,e95); + end + + if n > 1 + % Test for termination + if max(max(abs(centres - old_centres))) < options(2) & ... + abs(old_e - e) < options(3) + options(8) = e; + return; + end + end + old_e = e; +end + +% If we get here, then we haven't terminated in the given number of +% iterations. +options(8) = e; +%if (options(1) >= 0) +% disp('Warning: Maximum number of iterations has been exceeded'); +%end + -- cgit v1.2.2

eoUhvTixRS}IboDc|:iKpFnLl~PXQxsQKiwRlchIxDyRg[Q}]vmRr~7RdfyYoEUq\wUsr=eDSSYtEcrDavHx|UCbYa}YyxAaPwS[Vu~R~Jeyroqi]~OrKcUbRbSYarITrnXhazUeAeM\KqNeTOAaoCh~1sJQNbZtZXmvVp[hWeH^A_AdE^GgBcQV_NvNWh=`S]I_t9hEs;w>GjXy~LNxL{CnUlQs}_eZbSGhr[f>h|?GwByj,uGz=I_pntblyf}f|GsBW益ٜiwIaSxqLƍl[fQW}>mx>y[]FtDhWRfK^oLqXbHri{Sn~ngHHK__[fvQxA_wXz@paoKSoCGIRw]{KoAqSVQ{EXPgqXdPZFoJ|sJe?oAWEyUxlt?xT~SNWoL|Xq>k}VoCbMrKo[s\{@uPh>f;`Sm8\JfHk@?]9S7X}7`Uf?n8a?^D[CkGg:Uq;[EXu=uCvQZ=f?mAzOmLj9A\sTxMf;^<^dElFOIu\a~blpYhTtamEd@xJqXkfbbVa}qBW{=jKZdnAWxUggk]_m@i>i8\l2]q3dsKtYy_pc~ZIZAa`~|xmRady\nyXcFi;eM[;I_K[}WeEa?c~Gk[a|Wn}QowWv{UxJfvDWc:TjEkgGju?W}JgiJiz=^^cv_YnE]i9ns9bFgRuMrHgVjnVpFbU_}TyLvKVNOfJRi:Tk7UlAQlOXkEkrCmBUZanFVq=MqCNf@Qd1Lf9`^:Lt6R]:K`0Va9SfALkFHkJ`eEsy;iKf\mOtQUPUiLf9hZLpeePf|8RWarV_wOzzKvHn`lbhCLbUkTbu8WL[~V_xzQmi_SWXYxR^hNZi`\{>n=o;QA7l@u9kCrH}F}3wEm{nEd=xXmIrHpKbFlt?lCvWnFy3Z;`O_@k;l3u?fArcs=g5kEvSn>sHc}U_F]zEmMbDs?PQpOF~EZdeS]:\7kGr@lOehmHnTobmonjrYlYt@{BnBnKhNhLm|hadWb>W{5ZC`MUD]Du9Ih9Nv(rENWcCKq3TnEqUxMyKcf@ap[`kV2BU/?P9?\@hLiwdqSaYJo@U|F[y9[BPv5X{8Y~:a6WBWr=_8a}?eHwP|QNfEc@[xBbNl[UVRNr=c}>gRtKqKUkElIoQxemNrGyTjktcGhPupXyR~VWdtÿzwmihsmyzWN\Pf~[Ìͣ}t؂MZg8KW0JX5LhNfT[XLrU}Gg=bI]QpRmG\f9LWRlkI^l6CRLZl_k\Zrkvi}iovy{rUr9dj7Ym@mgBhs:R}OZgMfp@\qNalN`kAhxHoyCuMhXx]aF_`qiM]~E^}YjyLpAi@Vw@NfGOe;Nc5Qe=Wm:UnB]q;vu@]SVxRWb@`oD\v9]j8Wl5i`7Ys4YeHXg5]a4Vi8Lg8N`8Xi9kp9jHi|^jQwUZQSuHao5aC\tV^pDQw:XpH\m@lu9GNgShcwSZ~IdxJW;YqK]~Hil;dJ}UwKfTiWN[yp=lLVckuPyI^`d\[bRv_Zm@`s=byLkVY]_lJm|:]=a{ESLfsXTXSv7Uz*\v1p;[GZ|Ejo?=oogyluv3]VL{pnsJ]EWTh|;{YpO`ZgGu@Xq]rVtaoM`eY}}YtPbCj|@HH=~@{t@gzNr[l`w5VxtRu~9GuS{;rO>shXiuiXI\/Y~6o{1|Kp;aLaS[;l:}E`P};tAoeaDq<=jLo:aRjDoPe@q=l-n~>rXj]wB}CBhOPDL~;s7vB|K{>`MtbvStM`xah{Cp;CoHbCiGkDIwQE>mHs`{xbxS}]oX[@paJf@kWfOoIpVqVd9l@c@LoNfKRz+_~7Ww]WWgOHjCI`>_lEiqSgu:[q:jrKt$2I.O"@X8B^KjVsMkElOlN]v]vPYpCvh>TCGuRgd=[l@Jn;=cCUY>[n1dsAeoIb{OewS\x\aoIOn?Qj8Ym;Mi=Kg?bvCTtC]qP]v7aQf|Mb|Q^zVduGRGYr;Z~>cZtKTNDr[FfHSe9MwA=e8XU5gy@]R[nKX{BcqD`>iIUFdrHzFeK[K^=mIbFbHkyBt6hkbc[FdMh<]2UMkq-^8Ha^iERu4]y:c|?S6`vDZUZyMM|ERo=ruE~SrcntxPKyabJXvcQuVmqj[wBXS~NQGspD^w[yFl_zLxM~;XutQ=LilY`[SU}oˣDenj_~K|{idwV_zNt~էRKtT|Aj]Ucg~Ur^afK]g\|UUVjAo?^C?FtPEWiR/{A3mhGzT{L`Lv?xXBpHuTgZf@xNUQ{OisT5[@[uE^}6sBTDbIvTjDpMlEhpT}Z}vdcQSoBj}LqqZk9rBIPtMtOXoLwZiNyCzMIJ~H|aonQx[p_pOmVuZm[>zJxoIZwIt`|?}IN~QqFrDRMlLVrCqXm@Z~YdYz9[z2`}KTTMuFtIcJcJnGfQnOsYrZj|ctR}I{Se{m{`OcOWcjx`U|ӄrjiZ~N{ɜ]ngmnxg’ظ𝚰jltgUkC\v@LZ6GVLUlce``lSaN]|^pzaW}paqThv|~|uypW]sf|uGXh"^E]Z\xVShG_mG`yC_rK_wK^wH^R`lD]xHV{D`nIdwUgxJcO_tOer>UrINu@Y`AUp;OmC^gFRt>BvEC`:YV+Pp4Re?Qf:\fBQgAh_Abq9\oHOrMWjEon;KGNj[_]=Rm=UlFKlDac;Sz7[iFTkAP^FUgCanN\pL^lGVo>Nd8]]7PnB@o>E_7Ra*To8WxIS{YNvQgqAZ}:`GT~[?asDcBXyIYwGRrBsh7^@l~TlIzMdEqGtUuEPobgL_MYW\|CT@g},bNMe0_Tlz>GSP]pCl<<`P[w:^n5PA_u[tX{BpJ`t`jQsDnzixufgpbVBQzOoMMpm~OXd^b}|PAyqeGgwsgpowbYuPzSN[lU[lXaqo{FtIJ䓖ZINReTaWtLߞp̭ړeZrDZ[}Z‹]OzT:pLxt2ĭQnrtK|Xpg}>eULVRxbb`dwKuC~QqOpH[HyMiEfGbNRs\eGhKlPzS{U{E~\{mquSvEDa5h?b}8iMf3c3wNy^pJ|P|:VlYlSx\fQn:Ow>vDWi[~wInCmV]pXe\tNOiKkSt]pTL{Y^bDsQtRiGnLoP~LqSh֖|zw]g`weJW~̄mMX:_kS|RmyLFl:pEh@kU|Y{@Ro$5O!JZQsMZs68RQ|JaUVv;g?nJ~`gerZ_Pp^yV_x\gVtKsNOFHuFhLcyk\PEcM\O~HJmOr]jptpyQ{L|dT~V~MlGfPiQwefsqpyOjomq]YQWbVp\h`YkҤ眤Ţʗ||}||{u\qu]gMKm9Ie.@]8OhLVoAe`XP}i}taX}O]udjwzxkRkOXpanqobf{FV`&XXdvaY|KgtGn{IiKVPdoR\pI^qL]kAN~LPtN]fAgu>nKnVjSl|NZM`sJoa>W9UePXk>Sq?QqFTh=d`-Rv.LiAOd>Rc;Ch6TN6Qd8JXDGR=UY5{a2lEf[X=Vd:ai>isDosC^LdsHSrAXZHdb6g{@njIlGUM`lBh{=]{CUtOEs@Xj-Nu6Sl=Mn>cqDfz=PAexJbGiSU|?VlW[t@]}[@mwLxKaeNeO\Ci{CfAmikH^D]@_y;x8o/wQnW^SuLMhoSoirCfLUTNy[Pp=}n5`7ZD}v;PBg]yUkppN}SuZV{4[nk`DpBctfYmjSe_|\9f_mnSNoYJig|[dnaufy_J\]bfmuR\[W{*cP|Yc^KRsW|ZV~MUYwp䄒jTAn~Vr`NZXre~7Sƞh{LGYn7SY[SMVJFz:qXkwTHyPsRpRCsXgPq@|FN\_S~eFmImxf~pdgxMmIdxuS}vVyP{LsljJ8kǠquTkPdRl4u=pI>qCtCu?O`ČOToDsVxgTȴbYpZTI;g;x:j7nLlE`CP1v;~{jKbTiSQWuEzKkek@Rt6RzUfTnLx^nTd_l[amKjEcJiD]w\^`jNmYbqYq^hThMuSlYzPVn]v{zXUvJwOjIvUnKadngCbGjxhYvogQXJ~IXaahegu]\^yefqgXmPpUotnIVv6Wu=i9l8fuBNnIl^w[ib܋Ćw`pIfS?cD_wQqSeplvpW}JeWkS\sBHa:CX-YqNhnKZGexRlGaTZYThWZoGel>qxDWLbxUo{DaCfLiU\VOaWkQth>w~B_MYnSRj:Pb@hkGfx;\r9Kx@Mb=Zh6dq7Lh>PR4Fd5JU7JH/NY2[c3ps>sVgLilDht:r}NmJs^yTdJklSgt?nChK`IVQZzFm~@gHc[aLO@Ji?Ne4Ni;mu:f=UZVrI[t9TwLkfGgHXUkuDrDTHiyByG`g__aN[QqEcfy=qR|_`[vaMF4Od1po1uCkLRyP}MNMQYuZObkSirt>aDVK]t:Xx2W~4UrDlEhHR]bZYazDk~DVIktDONyX^vOi@B[}em[_wvged_\ai\{evRYxs[i`j~IzlaJt@FpJoG{]oLkKo=o^faj:x<`t|QidvMX{pʲ׀`gu`VLs?{FuGSF}PcVwPb|etUjQtYi?xHqcVn9H|:VeN|ap^IljF~qJTgKj|p}J\aVhr\I{GXePN~0deFuM{[ytLsfoLiMcBeLd>|NuaR^=eVwOrYdNlRV;b/TQWiFQ[ĄxxQkPwXc`SyFUd<]zK^>];Z}Fmf}YeBZy@c}HrpaAxFh~buUb~PlDkWrPxKeNbKzQwUsIeJcXj}uz{]q_Si\iDaJcNaFtDmbhgTpnt_kRqYjeeuglaFjN~VccmgpsnHr4\yAlPeuq`i>k?QUxC\;UkEmQjPj]{ІpOZ~r@QavjPtMt[KjTk^fMLDNntiKAYfNn|@a;m^\miXyATE~p|bnkgugQ_8Zs;gs.~KWjQoSPQyZyfrUwTbX6|@6O@TZ6zz-cFTxLx3{AzCq?REi}AvAz^c]WjAcHdFh;^{G_je9TrYJZrcTxV[cK\]LZ~cxd^SRhXdIcȬZrjZ?`.Uy[mA|yixy<>sXSvMV^shfh`WXkc\Wd^u}IyiXOdsOqOh9FVnPz}n>@fOH\~JSn~;w[LaŨ}eu\Dt]gF_y=]@c7|@Vzpv^Tq\KoXHrZygZonNimt?Lc`|t;6n̿U]j^;zIC^xQG^hgjǒOES`zt_XY]xEav]B[Nf\~UdwSfKpËj4=skÆnXSUuOhVYzueLmI|P=`Vj7pN{MeduCpaqVdNYyeWzFgTUXtL\}>X=^KrOeLzE_;lM~^sjxXnFhQűetYhAhcRjyKkN`Jl{NYAYzB\t7]@V|NdpPpEmUdY[ZhtM`@vJKZddjh~In{FaMm{:[?cAA^wXdQ;Om]m}R}p=pQ`ObwLlB|YoK{Ek>r<~B~Tq~Wn@Tb]vxxY@nRiOZ?`t)b6PHVm9ww7MqX{UDuMkXfIrTRpRsF|4kCoCtXfV=gd]qzjwfHѷSqVWOJh[a\mHYįYbmev|qvK]Vi_nQiZHgpakBQ]yYzhVTEgfS\c^[UR{lXS7u9=pLtUpd[}IEK^zdEq~Z}pJqx>wDZe갣mymifgJzuKbHc9n~=Z>xF]dJVzY[ZYytMiNTZ\{pJ]dMkto0vMuAky{U|a~mnގӘ|~}[xPZTMyYtlyP{fxPu\kLXsEb_^z^xHpB|D|DrhCo֙wnb~luJdRazhOqL]T~Y|AxCBkM}>wDW|bsmEvU^~JyztKXyRlwsmlWWjOgXzd]arKD^sqZa}b{tgUoKxQfY~`_WeGe;yLS\9h=i?e^BwOPvZzOOpYp^Wj~PtKe?wFtMDhOeGkU^S~XHmGTn;nSfHWxI_F{WxYyG{WQKd>b|FcHuOr=aMzde_{KNrTvcw]Nt@`7[~KY#3?"7F-fe9tp:|IcO\WRwEZs=bu;g~>iSj_pa}pdbSYpoW]OljbuI`nLg}T]uJ`xEdjAUt8knRdj4fw@h|NU|SXfJcu{~OHgajPTnkNw{:qT~_Sݟ_sqUbch[ygg_W]grDdDzOgKeyXgPWMnrRu9dQY}YVqGYtGTxEwKa>Swlbb4gw=iEjI{@Kez_cy{F|GIpZazjb|KzLgl|CN~N{TtWfXs\VCoo=[Rb{Uw|6gZkJbFY|4c{?<{J`eouNs~Ig\}XV_bhWSViUWF\|FtETSge_Squ:A<}\m}kzg]f[GtEPieZrKcfK;NjRuWhoWu[qpJwN:وxrCoyecW~F0Tqʺ_yS~XĒ\oVltБSA@W_QvecbֻcةnvNk}hYoplHRW\Pnr[]OwaXC@}AfM{7QM`goOXrn\viGw~^xStcwQGšGlΛ]纄l8zDZ}Ic7RRL]Qh4qO]}QXzM~aucxl^s9iC_4SKKqntf>x6PFZrƃT_iZd~Kr|ޝnkbO9]xX~_x=evGJ_xf[p?kai4qXWzKJpJJavELZ>g[k|zwC|`xYvDYNS]S{XslpSO~YhKfPy_sLwZjFP|ZY|_lLZizqRwoe\`jJxVzXJvYVЂoF`zx\Kl]Y|O{WsMpUfNs]tHcRkyHj=oTwGp~PqYm^r@uRdctYmS`qUrQ^OnctFXl;U}MyV_YhN:~Q_z4Xs,WpPuaRl6gEdQyWgJipTsZby_S]QvSrRoMfFq\{Qe5`6`}E`VX{TnRbCqOyd}cNeZhTnIrPrYsSjNoFY}@\K]}C[|=c\wYtF\Akb~Vp^W|h{tfpoxwrniet_mɉyf^VNp:eMUWog^IYf`eXy=hLs`{XdZUhLQWFVeDcqInPf?HV$1:%4>0Yb<`v9jrFmM[_\~QSmAPoE]s>zAqQb^ciVn_nXdq;oUcxUdrP{yCwQqOqqHbqFksNgm8_EhtLV}L\eBiiAs{EtHNgYVZmPkB?n]aNbpcb}YgrQ}Nbi}ffjvfxYnPYN\Pk{Ez~NjQhUN~Jt_cWT|K^t;hv0<^QYqp=oyzJlbeQl=k݋LydkDvQAi6˓>ROXOdQW|l^yNuW}JWz<^zFdHyZnRmYj>lKh]pHzIb@Q6?S7NeO@fr7>$54'~xGa;\OmrITKeyUsyBqAaBnL|Mt\og_lOzkRkPri:jAk{GiUd}Pr|KzPqMoyQxSqGlUrwSkuBeeLym9oChQT{P_cawUqDK\li~vlK{U}_dVfpPnl7dIy\VdonTxK`W`{\[zPTkHH`8SnGbnKn~HvV[_q]dOa|EiZlUztHmLoInTsgpHEThyQcRplMTnWRGR{HpoTyKPw>QyN~bzKL~NzQ^Uhm9tHs2hzFfJ[C|kRLDr[daUʶL|粉}uahTOuLoHKUt|l\X`Tjrd`xH6e܎sgmdxN]^QCZa{y{Li_tWe`TOeVӔtTɩܧ}ݵqKJuZTat^P\\nw}y`eyhbv}?x[_gwhxwDbP`[kKtXaaQW{7XGXEa]{Y{Ur``_uoyuvSyIpb{_|d^wcdsKfD`@YyKbMk=[t?oJ>b[mnKHj@eag|b[tgZl{w˅Ђׄkldt֘x֊vYkIrJgUsy⓸肸aSSlI@\MWb@MW3PQ-UL+HB*CS2KXLmY3QW#40HlNZahoNntB`~I~xFLzTYejrWc{QSgIhSY_POkI`hAq|Qw;fIgugozYk}bl}_we{QjQ\ObLsascmjKQyFgIzW|H_}Er`Vq`g\d?[zApo^zo~knOeI`Swiwdun~xx{rol~Džs}zfXuHrNlht[[e|+2@:GYBcoCor@ZP3KK.FaYyFe{/IPsm?\~MX`XpOmo>T?_lQ]wIl{TXmo~`^r:^oMXuOYjIlvIPLVsNanDjlG`UAgXv^rYYlop|cUuO]}Xp]nek{UaTKhZeIWo3OyFaa9gq9_G{iIzO{qo\g]rxTsIrruh\^fxPo{@q~Oq}Hb{?eyDjw8bKb{D`FZC_vIbzBmwFl}HuwI|I{T}YyPoLSX]jMjxH|_[Z[k~[oVxiOxSAScyoct}8iZGgR]noOw^^sY\prfJTdmv^H6~qbMpi|bnCZ|Oo:{}b|lWgBIRbZl{XkFqCarqYjrn~a}`xoYVn_`taHVZ|f{Pd~fi]xp]NAӤNgcxpd@ihl}orvd^KZ^z]WsƝuU)8Scb{rTzmz|Vb:yyAOτgE3pJgttUefcxFp;@{B`l]Nfhj\{Dj\myuKMii6]qX=dle~FAzlpFqm}vpja{YnYϐemwXDj[]rIq@RgIt?AqK6`GJxwvXIexLdoۡmύZPvBiUi|X`yb[FK<4\Bq@Ub]uMw`}`zAսz_^{C>Q_yJyUvFn@~9SpGxc^{AhyJXp}pWx8TVZcj^}XhtMRbL[gM|a~Q~a^CsR}`\Ym|X|TX\zOhWm]~rx]ZPهf^~Zd\rqmTEgjWy`wHfLPvK^Hw]sofbnQjajvzv`|[yqvXdzWY}f͟‚dtRo`aCgStGUu7gTNjNfJ[JjXkSg=g?kDa}AfHrbpe`}G]IZJXx5aIuLg]y]r_cIj]dUshT|XxSmOhFpSuauioWwXvd{uy^ZiUwWtQsArHti^z\p[jdzIr>jJripMhJmQnRmbt\nty߈ӌhV}QlXodZm@T|\u5We/gEl}LDX=JS5BX/AY,Kb2I]F]QDgk/^k?Vj8ceHiq;t{GdHfuRuxN~vEbKhc_}J{lKpOhZq{QUQk\LtZKf\f;tz@KdNstRWjY\[_tsPLoejNfuq\{ewDsLq=aT]|M]|6=[sO|JDbX[̔_tmjywguPXU[=}v@MQvwnx~GWVGJfPMiPBl=rѽUGpqEˊagϞ|s[fI;ybxL,zv=TP~F‹~YA\ZvGYStzRz\R_D|4wH投fkʒkHos.RPyaxIQ}RZ}VjVUUiuZTP^[.~tRt^mss:NyUp:xXuGOoD\`o`ut͔vXe|djZd}bqlSbtQwYvNTpLnQtEtJrX~i{UsR`eguVtPz}uLJPMOWewx\s^yYepth[szvxdU{eVr=5EVZqbPnC]]`gg`|gq]vljZcO}rosKJwuOgTq=\MfIsAWz:\;lFpIfb{Ng[lzFzwIqKmWuXsa_yOgRm}enBjTUnQmkB|>oJ\SpqLI{JX\U[ek4|4u\]ZnhFG~A]c=xv8\NtQPW_jhxV_}kZjOlVtDbcojanwBhEnEj{Ivt4u?W^kqpWȑuTgojTOiyUHtY|H`HQTvo8kBVmMLHQN~bHrT~JWB{Avdxm[f{lFOYEQkifp`v_LuTTfdhxsK˂~oQf흫hôPxjU]b\io}^cn[OAtU*NTQlFusfd{.Ɍ?HjPUk`a|/mAu]}@g@c~C^v2Ff\vynj|}lTx8DwBas^j:}`~{NMbe\܋U{aA^rxVsR;~qCauHzSPgJf¥uW^XWYgsZPhn£hǧߪzpS\u@PmQlSUFXwSiSxNm{O_qFcGiWsViw{fY[~MlHzvȎ{gY\a[Ycyc_Q}R~E\xMfPZVkeyܕeyKbz䫤u]XcjtjlPu_X`^[xbhlbqVvZbL_|yP_S]FZ?jHfAlNtXqOm?XnCiAYDUw:W|C^HpPiEr\yLeD`Li=`BmPr[kQtn`n^kHjaws[uSfios`rIjHsPxRw^~pft]c\jRo]z]xHuQtaw\yTiSeZzOl{EiGbB`tCeVsosLmRtRu_g|ٔ|{҅zƎ؎svwqlhTGnObTVkdmayom\v`ah^[kpqG_`q~n`gra_onadWkKyW;?ycnbWFsmGM~z瀆MN5_pɈuOi?{y}sHVwDž¿sřb|҆WjSDyFdM{gkympWp[trjJb}uʕs`sarWWwAwj}VyLW]mnZtMQWeX?_JIIjŒe]jn}KeiobþWqchQyigaZ\j[OKhJoglp~jh^{~siwŗ[ȍۥV\bdhdmIfwbgSn{`qK|PnStTe8Rm@ommw<]b~Yhmw{Rt8z}FCiItAiQv{P~V}LmMd`YxEk;MfdTtGoIV|Vup6kE}=Qajmn|=UddZ[ЂS̓等clcnqoі~u|v}iYbmQ~ƒa}QTShplV߂rpaPv@g}vpjcx^kNNPmbkdj>8y\fmhQ~M[ǍҠ}ň޺~Elꛎyo}ЭzXvXsVeN~;mK|jSiwcZiTv{;<\nLDt|aZh{s˂gyįz@WpوJ;LO9w?GwWgltRT_p^pՁ|cZxZv`_jޝ݅v~Mtlq`^{&=waRvWdvahyRzLO_vJTlxY{EưNh]Q`d\EmQbprtlY\|^ngOpG`Lg4kWl~@M]v`\ztryzVmJlIyWq|myx?s[zUa`zxa[{VzOVUQadMYSST^r7sPxG|Cgqovn[nwXnouCkqwzVr|nPj>j=pWqyxĎzvgwlteL}WyYtnW]dYibpgskcelElNsNv\isz}ww||slXhAiԃi}䂉akT{]bqQdyAg4qJyIuKDJeA\@Y?X:cHjNjUIgW߈^KwG~OgMw~K_ElUpZ^mgb]|ReR\Sog~Sp[{y`vCdc`}RmA]Qmfb^X~Eq[v_}i_Iqaxry{InJpHyL]teTlZzXoc]IyPjdsmf_g`~cl[U{l{vnj}]{n{cOIK|Dc:UrYs`RIRrSSh9Sl:Wb8_t@ew@k|MgfRz{DA]vlnen`JTPZm_`h>[nB`hCewJjHpQiMg^s]TMGU}Gu\aTllG~yC_?fv^uGsZ6PIH]TZ+SnDfPG_vo{Z\RauFxz:}\yVt^eWlC{V_}xRnM|DJi[mRowG{`Ufx[VT}^F~]yRZ||ZqXYCqQZS{NxQwkin^CYvbqr{}vqr\h{͟|^~r]hhXmr`cb\wPHazMruSb@q0FjWrK8yiyqަwdoenwѕymU]izPgMUƺ]ɇnVx`obИGPaZׁަ^u|Pmðlfӏjȼ{𡷴W{ǗUw}]YxkV54VDYiICfSy䞊Z|cnR@}YwhdyfrqPu]?w]nuw؍SecxNn^pW?mVdRo`x`I^XzOaIsFS\ds]|[q|H}RbVl{`s]joG}bE}LmWJ~Pk:뮑d[rcw_zV=gOHouFZhpluFޢ{gqLe=|Q]lL~KGv]S|S}DRb8lH\@p>SyXkjKkJpKnTiVL[eyHfc|_vevM|N|otNIkkfqfVmڗ}UzSrJscNnNm_nkӆ[rGW6hEkBhO}MgI_FkOzĕomiO]Y،nLY>iOpZ}f[}k]{[p@gEg\Usjj^d`}PsWrbtZuFfUfexoftP_XweesdqWmfmesiwmpuWW{[jv{uTtT~Yf`^_zdxckdZlI[sRuVnOvsΈpm`o|fZoWwJgEoWmcbRV~Y\i@XzDgu>fCmxK}MaXk~S}IqVq{qmyhoaW[[m]oh5_|?mpEnDiMm}\jLgJoUwVU`t_t_mX_UdLoFQajrfqCLW2jH;B;ZJ vk$r}AyIfc`IgdoRpIO`JrVq_{u?TRy}xCVMUm[X\WoWkZDmuIzfjY~WWwW}}PK}m{ad_qO]_IzaXf6Ao8xcKeDoCGn|ϛawkq`{hyXPLurqoq^_xmjdhozfa{qW`afBqtUKO\cNn76tqw}NuS[_gzIK~jnd_Rf~ɌTUv[h>}2ǤPbouftx|Uޥ_|~hTF~q|`dYbRp_ЂUv[{f+wAwXusL5|DahVކDecSk+>UshwzcyCX\m\sLvPa]^w}~3aTs=GyPy_6|M~Rk|Otn6@TYhh*nww@l}So`uhyXy?mOlw}eWmu_nDo?Td{Sjv[cQ{=UmXu?l8vbbfIjjkeH[NiAsPa>]lBTw4^?Sydxfef\urv\a8]?lTOuec[yc]wOjLaq_}mvsT^TytY^QmEnWvϾdrTjSnOlRl^OhQeNhMrNYTnibWqZl`}]tm~gnQqTjjWjofadhjVQy\~U}{̆fgc_ZoHkHhP[xRbUvl]ipLcPZsI]~DjzAg9l|KoM\Ujw[qHmM{W]s`fcf_njIp8kF^QbuN]iNdtHj}D_H`vOmwK}vG~M|~T[OQMyTft[wW_x;NQIpCMY'wY"y*`Nm]}CP]vu}ddR~LNd}yUb?LzVxdNj.XtUzhya___wOn_2Xn8[kJQnBdCLs|ZXtqhm\uRiJZiMuq8d8W=utMApn~j[`êsUmI}cWv|KUig(lEoZ٩x{DXYLNz[hfzPi`FyetW}V]sPuCuHV[gGUÀHqˊh}JgaFm-=ו|nwmzgcx0Jӂ|UxFVFϙ`xLvk`jaXb{[eIT_lYfRaXhHj=Dc;:yٛo`~K}v[~,q~1VpmġʮJKTlBXT~e`KTVt^؎obncNY|UWMmKWvTtR_vYrMyjVwXolmQbMu_bm`kpqbVt[SudvvrmyqTzdlww~nj^e]QwYXrRwVue{Zr|HlI[{LRwMXlAXvA]sFzhMlO\~B[YVsYlwJ}RrZd_kvF_zAt}D^Y\tReeCrw8[|FSwE[iE^q@ph7vr<yKX~UfTvgw~ShhpyV|CQJdlMa|0v;cDNZ]lNij7zDuepb{enL|}^zYdZnAg8`Y||Jx`Uc_nsJEpTWPc?Lq^n|g]nj[PnfTowMi]?}Zɟ:~hw\g[7ePSc}hW^fstFfa{^[cCsWBohPdLVc~`_zDMמRSeğv]vRouKhQQBЭjV{DŽ…ZpQ@ujdKD哙ލoZuPTVipTtW|\g˖bqqffrl6yM@}Pg}Aj6>nat.wdISfrkel48qfg[yLeJ}[olhrOZH>L|ͼy|RQ~NSŗnaĀDtcB{TYvFYŠySyu@}YkZZbP߯ŎjUN>~sqW`ҺVNO\`}gnģ~mXzOvnlt^Korp~adgydVcz`\tZnAbQiC|QapӬjp|lgegʀy^xy~ua_odlvzћdEgr~řn_tr~v^wznTpSshSsCgA[sAxFlV]uoWb{rrgyЯڀypW}mihrhxRuN\r;W`h`rbt}sqcvYbj^c;kXxme?bPiN}f_CRw8XutWfq^]tpwjR^`fpcpXc`wKRndvoVvx}T`]lwN\rZ|a]inoklpqNtT@`[y_SpSWs[r=^BRWllTmZyhp{R_Pdek2gPYPwp]]lpqjj[Bso@dU@{m~kRj~[bFn{9UkfknpJrmWn|OpLBxjDrt=5u[iXgXG`ʼnJv{rFWWzVKtۑvZwp~uIxʛwa{\Jro䬎`v@LMsܘyO>ƱPedhvt)McN{a~H_Jlu6f7w*mt|J[YQRSȩlxkyYsgu]qkm^C`9oWtcZeTrJvKHsx~xWw>ON\ockHt:䩄wji}v}]zclTKMUgo8?igcnuZ`Q_z{t_iwVfiLjhaRxvLaN]nUrlkx_UY}q^omc8k>fucUeC_VUxaqDhAupc`k~tۉae~zj{wetc\[aYfN[}2P|upIcbu{umnh|nOMPMS]GDq=VtBsFVUvaqbgkguRi_ucKsY{mz`s[>mHLR{QlV}sTRoUdRtd|MmEtJeDaGp?vXP~?~KuXUvJc>rSnMlPLnamkgqw`~xvoJT|`rVT{f]z@nKlWvRgViId?`wQyu\KZ@}DqYcWwdm^KaLZHhIcSeVpaYXMjk~cycJcDrXsWj_gmހOi`zVzOxLvYn^InSx]iMiQ`WyX}UmAYTlXsSaA`MwPbTxi{e\rj|pWNjiyby]jD`T|ejfwnt|mi~]cjh^uetnoV{KwRJ];Y[UiDZe/nh,b;[~WTjcTmEYf:Nl8V^=jj1[{CVnL[gA^u:lqsE_~Z`zWSsO`mHx7Aeev{osuR]sg{k|vim^oLOT~sIbҐe{ibrTlJaUR`vtr@yQEj^Z_nsDs~L~z5{PpEkPF^_^}B-jTXX]Jez[Nmdlox[mh_qdwXcHm{:1rUCjҥiZcjQ\\GnytwRZHx@Px?~v=`R{ɩɁqaҹp~xalUiVQymif|yhk[J8hpÐ{oXPkx}y8o4NlTnu7i8m|-PzHNwVW`fLoqpLbJx?Vy^y.1~nZƇGm`CdGAA{XcPwr0~UfnjʜgwWxo`faxlUņ:nڻw˶klc\xktlkRfvHkT\S]C?|GocOnRt]sjffg>ExlxץBcoJ\yGmHWr`itKJ՞jkR`QJ{TvyXrDRgLTykzacIkVvlrYopl^{O}rYeu|nxiZtfuIO|JJ~gW~Usc`D{x[AeaLlb`Co~hxYgdOoQdxgvy\SVpllSg{LPdk}_yOcCvEj?Wt>e|EyZdyTV`h~WPnS}QfGy[p\|]i_`puk`jE^}KgXvbpnppuMUz@bK`pUAb|<[rNz]~WW}:PvPclAeUoj^|h[toNdOjv⒜cb<]Ui=VtJangU\~VWqVclB^sBjzAd=\t:P|HPcA]h:_m9osDxEMzi_TdxdZ\blYVoNupZHcpTrn`iCau@]{KbsOukEl@rLttMVK|qLi1xF]f{|dgs]`gqM\_}vhpcx]rU{SPge|gm~zVs|RxehMi||WRxopt|Ez@teegiy`ȨD[܀m_wsdwxeqG|Uwcwh\TVlRpHmH~RjVeNW銘|vZ`NgpAKhZq^^yYavG|axo]dZtGsA\o?k@r[`}Euk`sv^h\\~;tMu\tvf\fƒNrϣCxVkPmL]qpiNU]Om>~DFvMr`wV\t_pvTvQXtErKfIiQuZ_lctma^c[sXDucrLX?Bf8TnPXxNjEp@fP~RX~cOv|מԇ~Mb?fqȸyFmHgXdJYy4Nq:]yFbOnH]C\K\K\]pd}qmXkQkNscUgVk^^?Sw``S^qwUg`Ok~XyQnV|arAhN]}Om^{\~D\GnFdWp^eB]JtU~`΂RV^c_xkuVKoRc~]|n`YrMtIj]_{JfKf^^fFmnBnuGSJIjMLU=]c4ag@fkEkxIjyH\S]|Gk}JuKmU\dVrXcvHmzIkJXKKpT[iMMa;Pi8]d8nr;HSotnfWh}jpzVu?Ar\pely\emJgcDVB\lC}gFnAUTbfn|B]xSeanc[khjHdiKjOy:K^hr_ns\Sk[hlVlYozjeXL{pIlW}fCDyWW]yVpmHHWc_^l^LgmvqicjWYbn6aY=inu_R]_:w>>^hnEfcfzzZRv?c{+;brA3aJSrݳ\~|PLdm[|O}Z[x0PzM9Q]]}ry]WUrZFwCS^]Ï~z~^HŋfzKx׍Ȏ[n=k`pCcuF[NtHWwKa_libv`sQmU|WkOfOiPjAdR~WlURshqiqas\\RVzUkU}UmPhTlbEcE\LpXjuKnLlj|ojeiXpilUsKk~Zoh[lJmIqPiWtZlNqbpfwAguFiqNT~Q\bZGg>Qa8W^0NhDcbCfyEa~QSWciMm|F[S_vWT|ZXpJqv>kBU]FyYneE`2OoKl]:sp3czLiLUt`hXyxmMKr]g~}mZorDmJvSg~JtGzA~EXjrstmH]ntHqX`Ixi7b}C`d?nuAbY{KZuek]]YtasbSWjZoρ݊p`h`q}`BEgﮔuĥO}Ҩ΄vj¸[}u]PSjQLukQaHx?Ǒn׆~atvtZGsOŬrpơbf]ҳzo_^z쭉ƛNS݈gK>eDJoeOX({p+Hs^ŐZM]{FZHl4vpQ^klHS~E_^3~vzfu|MZp[Y}1^]uzRTqp^X^kcmtK{{CM]eO˟c>udF~K;L`£c_zʠ|FdKcMjuHAr3a(tV_N]}DVZn9|h\{3mx,cZHtx,Ny\kqyTqP<~dF;g\vkldTv7lH`T_Uer5XtvmhS:]qQbpCTwTogWtYL~{kft͡_|AhrBuOyL_Ls3n~cZ}`t@xBx[c{Ofh_}vEBظZΣ{fİԫҫrbpYޝț{ɣhM^UАUT}iyj|Sa_gPU|˃dH{GcwVd]flt|CJnc~gu拦n{qike]mka>r_a\2qA\UZpQy[rXyicEaJnJnPa}SmI|dif?oDsBiEZ}ApXfh~\tQhMvTeCg~FleMgDfFgnv|?_M{lwIbyV`y=Hh:Kf+Ro+Ll0Lt7UuOo``WOjLToHpTb\`ak\f{PkzRbJl~TtKIPReiqH[wTb|YaYmXxRmYyeeXlki[yPt[w^ovbiRbJpWm|OcQrVob]M_zFlvOegc{nz]~epao^mk}swn^_xjOoPrXxfuMiJpFhPqLbzDVy]ty{V~J\pTTkKPnOc`H\r8RdAQg9Fe5XQg\uWw_tY~JOUxeirla:gqBwHyBۊVҎS~[oY]w]ufr]|KqU_|NuahdCiu,}?HXǨT_iqrkfZryLDwVyeunlta^g}vVF^fYsMjz=K9aqn]JqkVlocUgŁpoWQU^cІlXx~vbdVJSX7qSޡWбZkf`idL{DmWmM`~apf8`:l}FDǛ~k{b_L|Gw:]쟡{h`wFABYZTnEam0ҘC|`UwvVièk뾡|~mFx;u$W`[N]ٱ`y[eB[ASPFh4r\+kl8bmP9XovpY4QhK_{Ff|VuWmnQnoBssLteut{osӥWQzsUlϮvȿuilazaxZ|c|nxeli}ВtwJ\wAuRhU{auLuOsRqXvew\yRtЈ֊yycub]yRsRu]{{nWrUzgkJhOsUuURVYXxIev>Y\zxsdG\ZQqLL^G]b4[s:YqBOuCOfA\^>]s3_mFTgCNoFW]8Sg;JrDV[GNa=R[5mf=_~Kw}cxX}kqblgJsL\_\{Xdd7zpDIjQ`exGrJbr]xVpj4pr;yFwo˸mc֓rS^򓸴ttYptPud4Hldeǡtp`cC]zXYq_o?ZOmy7f7TxewWȋskc@mv`ֵFVLiHw|cfe^^5tYfgyGJAIal~vf}{WR~(»=Q^XCgLn5l;r.oW^֫u]x]`xCeE[jwdUSYSOl7bf3>k~GۖœyvnSvXyk|=rN`<[oEN}Fss3epB]c[}Umn^][v\OxțxTta~kJj@a|>rCmMsNp@tWwNoQwUZ{KoKVMJ\v5h|AtO{Setzlj]sJ\s4W_)LY>aqUmvY|FNLO׮S^gƜ؅֒v~kjSxchѵȃ[mlckewpcl>jNWqO_T|W^YZ}JoPqUڟσu^bd[X\n|ow^ugfXhdMlM{ZiksiXrLoGt\xibbF[aNpPP\LX\5\n9]q9]f@inA`t?E@FVKRF:Mk7Pg:_g4OyET_IM\:IU3C^9K\4]ZGisCcz]N`Mr[k[Jps;OVNo_Pb6ea1tT{0_tDzMVjtXbd{Zv{2y:1loIΪ\{h}~y`cCt;DVcy,`NJКyfcS_oy;qU}ERgS_O&zno~1|.>u]UsumraYWqgKecmN`0ek-X{Kiu>b9QlLje5VbiS_Nt/Rw7Rt(PӜa8ktPbi:[oƒ|M]V|^iXApfuTh~K[Icp1TfPVapcZZ|j}iJun`FbƫܐmVX{$y)WpWYcaq)i1ovR`9tGU~?|InQ~fqHFYBzK`N`X`~b|jxWNi@m}L^JfoKTr.l{39oWi9dPt{dk}I}gj=fm.\GzBZSJoqnnZs[~T~ddWcrwh[q{tpfX[qh^0rB|Wj<|{^|t][pONV}hBBuviiOcr{HnmmvlG}]}Tt@^z@{\qEgGaKmj[pmKg@jN_TydjZ}k{j`ybwRhDn:RRmKsYPcF`xCb}LvE[r\zNoGtZ{][WX_\YIW~:^CrMHUdzLbL}af[r_oXX}f[dp_oOrhwpQoRYi:kKOLvWƕZHoN`ڭfعimŷčweyr˯޿{աvrsƋނ̅ЇvoM{`{U}L}c|cnieNV`yԒ뛹瘲ؘɇmug]v^kg}XlVfOp`րw]vJo_^}͆rtOqP~RsWwb{]QLJvXGlQEaDO`8]i1^q3cn8nlC]HOtQO^FOV4Tf1Rs;Un:Kn?NbNZ^@Wm5Gk?M_PmDn`6xo(K>ZlNXiHzq=MH2_dcE2ww?S}^eRyL`Rv?qNYUMxwlhXe={9R`m`jOu4}cRȌݿ~Y@hء}isԙykGW~oWdhvᚖt>mⳇ|zڏW]QTZp2ZxMOsyy|_s{A9_l|wL2pmF@}^OхSeaq0Z@m>nGtwpT\j6v\ORRw\qBg4bUpWi=NsOXbNT}=Jq8n_m|[qV~n{gUsuz`AdGƻGoiYt=h1n3hBrHrG}}jĭmfSR|9yDTXtX_xXVdI@o}JsKsWv8KV`5hhDxYay{wjg}6{Ri@j{C_IbxPbہbY]WcsXoQE|O]~Twkw~YnR}Ko[panywhZpS~VGn}AbhMrxTǔxPm[zˊRg~hҔhjrovyg_[bNpSs8zBkPPu>kAsPkj]imJmUuT|}sT[hV}^\tSwUY𑐷{XqYhNWvQvGxLHkD|LLkbjjyMs_]jASp0_|EgJNggZ_nIdHz\eiNPzXkgwHvRucddLjxt[_YeW~cQn7Vkyǘ~ωɒۄ͌ѓ̻ª귟ŨĽ˴qĤ⼓Э{{jlaYeis~Wg]]hvۈ隮ݗߧwpcxZpVt]{wdaQ}_~ČÃSqRiVoez˃grTg}Qt]U|ZzUoQQ\Tm\`lDPuGh^?]r-btFbtBlqHaJnzNgxAUoHknAH|@P_?Uf7\g?im>S}>F}GB_ANZ1MY:X`1KuptviLolqiwSkNlLTqQg{MwH`ܔbYXpKs8ZDPuS~Qz}Tv`yxRsXPqwX{ށWq^fVfoCuyGQgR`Ebs=epAhr1^vBpjKCNz]jW{h3{5|FHEcicdb{Ujp8s4mA{]¹bڅsBOSYcjuejrXOq{ji{T{SgYw{>\Av>VpZlua\S[qsaTDrkdt.p>w=NXngfg}7luH`Qgꅛe_s`D{c`{_|dseb{hup}ѳNÂncC^?CV?X'f}TNTh{/bTg|//Y>gblCk7HyIORmp\O,:XOtd+mCw|8cpt2|i`c~WJXRNk4qk3t?Iht:jr0D{Hd󑑭g`Jlr2r9jAdh/`pKTi3RrFat]rKIyeY?c}`vIfq9mr:u:\KduTapKwqBA{TjdxT|al`P`DmZEdCcf }&i=bqzsVFds|_h_{zIYt`ud|SS_jmwmr-O?KfySzwLpPuvJG`qqv{Y`jrQWb[VntIWy9MjRQ_1YR1weZřzedlkH~u9~BpyjngK~_t\~QJVxutYZYqs[ysE|[|dNzdz[{;_cLLՈۺvv|nH[FUvIRy7V{A|q(@}PsN@{w̾cꀌNleĭ߈ƉޘweQgֳQmrpW]rvG{P|RnKy{Irf^[ca:{,jR}^dhuy8͋>kTЩmjRja,w_CaQ_yChY|DbjGdu_uEXu>Yy2Jw.FeKc%qj)*d~=xX{ilyEԄیXhbMsWdSYy/C_/nN$@b<'8qaamD~~RdxLD|m\u:arNYv~Z~ݖ{\qRcldi\wGj}ddxjJ̽pWkAWo_sF}flhqOt[vj{I]eTuDaHꋓpgqjYPjvOiiP~HqGjxVSFFiNn\|:azNuȁbewEdmHqt@mPeUdwKjySduOkOrUKdEhTEWCpci\gSk>^zMao8oQyWsd^ZlvNzBdwgZV~Srf\eYev`/cjFo\3d~+vCFori~Ck>{:醟Ġi{l|gwRukcbk_HXSk;w5lXYfe^Pi|AEN~eQ]^t;\q6]a*rbHfXzPymzs^~`{^nPx;x<~Oy;GXkaBm9nzrjjO8boŹxć|LwRwMXq}]F|eazO;M[;j7od&~ELzXms:t8x0@x[wNb?{x VheaRRe=rk>~RB҃x~pqbtj]{[Fp9rc#$f:-x<=k<||Gٵ\`h[tt|IojKtzR-gHrp8>Wrio:sOSrFYsdpMcSvc}yr|ȆRijN@kt8Y?VPj>_r4[k5cs?hkKkBJgB|zJk~9KƃZrpHWm}Z|yalclFbOz^Q\vt[hv^|4ChquI[elOv:_rHvKsRnŀ^b~UjKoOXr@oM]fEGj~LU^|cu^N~YZdDstt{TyJrXJX^ta|vvxzxWnHomhZYd]Uo`RxHmYoPShg}eklPydƖzgR_DsJb{K]4xL`daSuP}ujtrzq]z@qExUlW_~kyiZ_jzVdZl^rV|LdSlkńx\fqȐU㏰ؚuyOt͈Uo{J}{QOopvyكddThy~yWyT~lzlpjy`vPi_yj~o[dsПᎵڐ}nؚ܃u[`~wÁЁcpG^OqhUyvqav]uEtV~Sgqxol[~Snfsnp]~dheY~OjWwoSV*Tb/Vg9bkCcqEbxJllMZMZzTwrVmJuX|K[iGxbJ`?QZ3fd/ty9j|AZ]jgU`~E]MWkN`o>So:SjA\f7nj0by9_O_nP`q@mxBv{>pV\fW{jQvZUwTjqNRFcsEUu0GdLZ[MHx@qhJ>Vlk|isN{]hVcwf\Hq0Vk7Q;?{}jm_Sfn^PPpY^fׇxۓYOfy4K.2@@\O%tu4hAg;na]GkKUQğÑdZvt5sV_nuseV^_WjE|h[n9Z}#Z@h3^0[t0m|SoqHr:AiP~FcOPyB\l0VnP~yERHf@WCeZ?Y|PI1:dx]ItINV|ai~kX5]n1es3Ck/qucgbnl9wQpGt};?I]Dbl\m=_[PRv^yOPoh~LtJbduQhUTb)E^uqzbEx홑u{UXQ:kis^n1w]nXI֗otfYiXt~C{:zblMxIdY1f\9CnEDoLgCZy1s9gyidsm-Ds+`>}}=p^z`ȣϣ҂ZXAW_q}Q]{kU~Gav1D|2LR gq-=E|Xi6sC?YTR~otS\t[qBWxUjvPxAdQ{`]OQJ`m_pR{Sam@o>hzň?Ll/agtSwCp]JkPGWT`}ÖeiOQlk_nzi_|GrׅsZpUf[x}gYofJxFQ|VM~if{RIkayvQi˂ÜcyMhy_x]]muޔy`XSrBd}wehDja~n|a|XbM5Nk^w?XyDrrvzVPrVI_xYToNoIaGgXpSKlnqmtZjЊiB\v^vPsjÇU_oP_v=eILlgy~͚͐w}Zn`ʖesMuWNoSlcpvnrf~vop^|OzX`QzTw[cIwO}NjSsQocnzXwnp~mmēϤ箱ɠŅ|b،f[}Ogbsgyleuc}jrTsRfS^pq|[tY|]nSeQkDpTw`uXr`\~oxhXyXmdM[sM_lM]l=KsKTdTs[?}6pc~q_X^z_RnFpjMrCqOvOqQoc[t]Pz`\Y?as>opHcw9UzMcdLxq6j?}{QaOScwZ9k=lzTp}LUK_kRYBKv\WfKvhRV;\xXc6|}4}?tK_Yer[}xPDt~`vbpxyM{X?f$o+j2[87}]RcZW|J\QT~GXS^}~~]ZSgXPUYNus|xQ_iyzg_eR{v;/IopTyJDR^s_|d~m\br_3:4[eoc~\|h*fg;ht:BvZaDSqG]vM`ZGoA_ReJmtTU[ymK}sPoD@dtyH^nY}xpETbUuyDnRT<(;]isv^gV]@O9tl&|~h{sf\j?^HaEk-]j6BdWgiR\)2cyyB^ ^o7cAYy={0~q}<2t1Ž{FI{{CáX͆gnob>|[km~|_Tzf~UWpj}p`^i}p|Oc?w}CFf`BuQi~0ŰXZ^i_DxcrTgfs9qfJXNOl9]0Uw:ee1sX^^\8]T҇gURNlpX{38g}ObYt5sRy_p|PvMv^Nuil8vFoc[POmtg~}T{~lJzatW|]v~agmmvK77j8Um2tDtInZXrOYe|jl~Yq]sK~Bf~>rc}{sTvUyV}bnw{ouUVp_PLo?yst[Op*rw[aYhfcl~nheNt_xhsXg|V~K{pz\ryZm`h{>ezKa`nk֠rJ^@UvGaIN|0B]2e{R}aˋpTӘWiku5pGtGbCoYo{emftc~jvkh[i]bie]wScLczj~VnWhVY~\bfauq~eYyf~MffbsSZXjpGbPn[rUhtq}{coŕêа肎ԁ{͈̣̆rMhOsTomnmpx_sUpUxVpeaexwjRLpO}YR_xTtf~corkZw^niuS[~TUoGVfCPnEXhDna5}8|NgjWg\jVhmHtF{Psohfi[_jTl[SnR^eCh3sEh}MWPXkJif:^}CT`QoFqwK{zmܗsT́XUWyLkiINxN[cGOW_}Li|TmDmjDip7uE~Nah^dLtJh]7{u7~gm}\qZ{]ojO`I{wQ|>pSpsWYSdcITtux5?★ڝtoAsRPnehYMKgrQ{7fZrrlnkjmwozie5MprZU9W|QaNny:sxQ1EdUvgyLv3iZcހxةXcWwEVs4XuC1UZgFP{Ops~:EGvi|䘅W`trCvNXHnb9Abd?EePKi?lj1x3wJC^HzT(ܻxЬCaHC^BU$pU,=yW=S:]l[F^OmXƴhӎXdKq?KSGps3ruDw~e]늻ǔDF6mj&gLm:vtxz̼;`bǴKmAwևݛКwWzcnA9W:R&Xf'jp{v_unC~iUo;`fk`?V{SVg9zE`yier>uH[sKcu?qu8vLvUq6pInmXi?qnE|•wƅ]{RruS4Dvk}On@[pxpo{GmPgAd{8]donY|jM^j>X9rBqvXT˩ʱχ|KnvRzbaCl>jNofxx~y}{cOl^OX`fBRm@[`^~Pf}F^mej;SGQzcwnya[݄pSTwAsQU:Wvxk[fqB~edSbazai}\iVrhߞLieA\kB~LJMVwBXQ|NauyhGVt~H|Ofmd{hDvDQ\fnG_zLJkHSXAhb(iq:xp3g|DauFlu;AStk[S|apbkPi}BuzFDxVҎYIj0o$.>h=P1rO[Mnzra_hC_:YEjQ3v>jkso~S{VmWMPnpotvPb{\hjxPcfþ`̺{ggSO^h>6ZjxgfT՘g@X_v[OTem}\=vmzr|^mUouFjC>x{wimR|M]pxۙNwrbc58ξ9X][cKruqÍ9|֮p֒φ~?M]V0o2^6|cCJĖcfl;c҇\YbqGdxZ_HDci\YkL{TaKaxhtVeLlGjPkQhNW?^tKj~`D[wLnguS}RiۜPZǩZerApc`м羼wȮZIuT|VnERr2iy}ȃchupoHpEn[pTNrVиτׁ\xdyu͔vjء}kSg9Or9Io5Rr4MdzȦ}x΁qapfxXy}Ƃ[e}qcg`rRgPbFYrS`byG_zRkUaSjQwbxVocwht}irZfJbStNmZ~fNhSa^mg|UjYyJfOo^[~dq\gObHYLb__Te\lXlescw[vXl`s_jZpPp\pqdGZWt_oVb|Ma~L`xUcr|vdHeySvxM{~I\ggl^leDwmDxCMNqPn\HfJ@d?O\5\a4u_Gp]`lxUQzi[{gl^^|HK{JGbJD^.Kd<[i8fh6vK^Y`uoYqMclIZ@\rWPrAFl>PZ.V^%mn4z:_May_io9Go]\loZ{ETl_o{TpJr~K{r?IaXm,m*XzXP3|}ENFpnuqWZMDnWXaAhn4}<}exogi^|EuLdVS|awrZt3yM[WNv_ggBgM]_zbwh]j~9f_}?F~ŀsQofTŃ}ՑbX`ZJl_gkuVNi6H癊ρYwE}D^zŇPCĒxsoO~Gq|>~b|XsXyWoyabvLnic53EXl{r=cP}^ǓrcWj7lcFs|KfAVLmmf|jmr~iAubiZnyfx_뀻sӡϛ_t[lvV[T8r2ѬlI_y]xhwg|E¾klk[j9{OXgGh=wM_KgEnIOqY;{:Rx5[lUkSc:\{?wBsIdkqavE`kRvJhxQs`hEdX[wCOm5Wr?em;Py9`zOp_oRfvm__[]dʪ[T^;v}fqCsloYQfiRtQz۟r`Sj]kKZv=Tz\cfqvnb~w^t׊בÑ}guBWpAu4d7p@o\fey|lVXyVrbkdJUMbhtt{]c`uc{hsOX}LY~Lm~DYvYVl[V{LLkC[zEV{DZ{Od~]me{\RakoiL]~CY}IdKh]~w[}MpRj]t}MoReVlĐr}c{Sx_y]vkkOZzASwCZhtX^PhUwpTv\}Xo~XoeavFY~Qwb{UmJgyI_tDVvb|qqRwwJdyPelMohJewMloEgyEhvEljMIiZJdUL]28f4COighfʉzcfxge@|]futRMsJzb:^FE|ZŋTaf[]Qtt9=_}dHnMwm7^b+W#^Zwf\ae4bQfł[qazNsKiazFIle_􋧿qzʛWYodqDqtMuHyȫuؙopp]UܿSҜB>q_]?c?bL[~=asBgEqmCj2MLJT}Y\6x)FB?@xۥֻٷlpɄa]lZ^qs{WհQqPzmiWlgvBCYKuV`'k`M]:G]/?\.>R:=J.WK(Kj2PhJldMn=j[^wcuWowHT}`ruidY]UgLPbCTZ8Wa5gi7p;oP^VRvPKa=Og,gd+\w.dnH~~@fGdWWmCT^<^k3ol0Y5j}Mx}@hdwP}POqce&l|(PnYH=#xx-z{L}ncD_jgzjs[bgWmUvmC?owdvLsEoPRTebuyP\woPĒ4xjWw\sd||LyVa^u\MQqvT{We^_KcMKGqd}t{o{[c靏^yqnÂzgruvcRpF`whMOo_J~C[b3^f8pP֘]bEOx~PaRV}Jo_ng#}:bqQGcOQLhxkm|ibog]X䀿Ynywer_[E0sbyjF{wJ'r6Zb-=KQUqcq6fFʮ}̰_\~M|7`O`-,}ؠzϱNpiyqXzy~y[TΗHQ΀[u__fTw}rL]mWk~ZV9aw:o]Hf0_jLmJMoQTrOr_H|Mx@xEiIxO}ZddV\AW~VgFc~b{Pl[yuvjuEks<`d2NoBacrA\|VsiYrRz`mzWsSwSle|ypsȥ_razYt]thwcdXpf^hZm\qKfKnWtuiw_wToMf^lXyKlGoYwbr`RmgjdpSzHu\fx]m`[{aJpLDeI7TG=M>GK,:Z)=J1;U2MQ1[U*Dq4HVEKV5dP*^pt}z:~eKuGNB|hjVL~fuuRzfrw;oRXXVˁuWWg}Gwp{fNhȔQk`LpOj7i?~JyVhBmvFuZwuzPW_CCJUk8fY]q'Tn[w8lyYW8\?Tv8m}L^~Qp}qmQcjTvU}IzElLY{V\g}ma}D]{@Xv2r}BwDWnLFs|XgOdnj~Oii~ohXlPƆfg}wsoxWXu^V}W^[{NQq@WuFmKMr6;W2AX;i}LgQ[v{UxMi}ipbSSu;shniyƒkr}ݦݕfcrhhXz͘vxz]kFm>\ETwAips}|hv}Ɖ`ss_Q8RxHjXapKVfCZjCNf:H[S{Vh|DkQ\OcIqRvAqLk[hLmR_jUiIkWgybw[sYvQqYndv\|q^cTj^uerXqYxYtlZiQmO`DaGdy\mZjUqi`Zjf|[zN\u[f[grxo}g{fyhwVEyL^|hZqreghqOQzRY`J@_;?P=KK.A[*BZ:>R/:T*=J'/F-<96OA)aPng.`|FUrK]^>Yf5SlAscNg|APpMMbA]\6gj;lr9uGk}SSfLjWU]BW\7Ts:Zn9GZ4FR5T.n-qMFZpcU{zHxEd_^RUyHRZ4qd*Zv9ylhFt8f!q+NhmJY0Q)H5B^PzkQomztoVqRJSioX{OupcO]ˆfbFf?]L/y5hrENk|cZ`EWP[rEpvAt?=csVHdea8b>DAFi$Gd,GaNie$i;g}Fy?{VeqtkIjEoQeSIpZ{^lRr|]|JN_Qs~Le@PƜcPjl}acm`x@L_s膁YXSvNdc|}Kkl`ݕz`wѕcbpRsiwP^udIczfVmd=c}U|pBRdht4tA0\{Vo3jg0j+zU{{uzH~FAllW)ĉ@bE{du2iW͎zpUهៗcӛԃ̃󥆦Zu􈮲xt]bir:>饢M`wƖ~Ҁ}N[r8Mdz}m̕KTsvqwlKcYHvIwQfs{zHƄJn|釘N^\CdoVr{]aBgLrjZ]ql=o`fKMkWh_gOFn=o;}Riqsyqb\sSnCy5tJf1Jk*vvAc+uByQi5}8Qa7\z*e:zt\ObGVFdgGsqQ8Od+Zn/}LJ|foLKWz:^u9oNhEfN{JuT\3an?wKjO~WwUqSuM_8jvH|Pr[|ZifvhvPmYac_Zn{V`gKpOa\o[Lm-Wr7?m&J`.bH_x1Zs-Iu*Us:Z7Ie:fU6Q3k{TrfgZj\UZdmhΘzLi@~ftxݮx˓vpimXkpVyTmFfYrtb{j|_SiDPl@az8Tf6\nG|Ba~=UiGrNw[bpVVxGjP`U|`oNfMbDd\pcqUl[p[mQp`rehoizvgjRf^tjz_rakcoXgXuWqSvHZJZQhMgb~gYy]jYcBSzHbYnbym|_bTf\sYmOj}Ny}`]]Ija]_`g8LuIf]OFf;BXFMU;CZ-F[BPX31a83I))=#;1]=pb%Y6aqRnnKmEmyBWzSg[Ilp:g~VXxT]^9zo9qCnhdiYvXQoS[c=Oi3?lEGYEHM1LU3[^.ch$blPinDh_WO{XwkufWRPp?Zj+\f.g?m6q3chz'SniJK+>%M(UjYzlySsh\fOQ}dhhZrPhugYuƇsiGfyW3-ノmx[fwcdbG4Glm~YQjvxGqDaOTHy-C>YK]pLvz2TTPukytbHya~{Umveo[v6EiT~`õEr{iԜpragQugLARLOCuwaIrr̈iOsAGwjgaOrWnedmT8xtwadeYskBvS~x}oyC|vS\ċkxnj_A|JKhwTm>~FB|Nll>p]d=ndujyqwѺ̛Uɦݵx{@rPkDtSȰҘLƨwBƖ`]༃\xƬ~}kyLczTU^@vrLn}HVs^ye^y49Rc>T`x=kxhJb}4zNydLUjfsMȣf|bgnYLzANOzS>BJ&TI'yZ!9cXYxdahEbBn{Pa~OcjPomGqYl^vtS|Rm^iy`xglxUmQtJNM?nO>\BFO-KQ/e`1[i+hi7mf99N]|hmcmgZ\Lx@ZW5o/4n.a#p|(SjaED"N$k*NU]rc`Osj^E{soo\|khOwFppeW|osj||cfbOO|yklshHեMة{vo~]?XrpfhI[kfyU]y[_Vm{7vgyϟXxUumyp[o:aQ|kqlL|bl~z{~Z|Cvr{HaDS|CwJy\[suMgTcv8oEvDsLb{/w2~=qGQlmv\ķۘԐbh‡yW`]ԁ~tckKy[by1o@g*cs>]gaVy8]p@`kBXeWoBbJW_6{J@MmF`nPqlJnUUx;wqeuJuNRq;Zp=YxEkzStGlMd~EaOcw?rknJitSDqF]k=wI`tHXjFWpPNk5`wVOV}oqROk)`};_SyUqwm{_ufYpOnYr_mekfqephoscvjs\uVtcvaZRuu}r_~Mkz?f}>^wI`zX09Q8CN1DR%ZT4h\2Ym@MiGW`#Nc{9j|Bh~@xOVaiZ{eX@rg}nWdP^waZjpolmmmaNlzZwyIwDVohYu}jm>pBWOvpoPIgKjiN|PPdFs/΍|ћmTiRu:lȊݗy~xX[rbهiZyJYbˆVO~p{IgvmNȏ^zoiuqUCV0TH`h#Qf3_jMp|Qiq2.QtcvAu&U`bnGg~:}I9fjx}RutKwJfAz^7b}3|hXZ_l^\PIÅzvr|Z{Yw^uZuZ;iaWi}/qo8k@s-Rri?z@^lkH?[ls~i{JLlbn]qtIUÀ|cjvwRy@͕]ڝ~iS=zEmo\ayE}Wb[wl3Zt3Yu({Rm{KkEx[VbuJhAW~7qL~XfKZt`hKc;],Xr?X|?XlJUtHOf3F\8jMt?kHMl2b{wPuZzox}nOpc|vny]zaWsV}iyY]YoimvezUcWtMw{Nx]}NqFkKiaxqlqNk{^c@lMP`reSeM]Kc\zuhTxMvk}pupiomhWcP`}<[AfE`x=ZpFb|HeEf~=UnEYtA\wLgVjO_}OlWoKdCUu:Kf=UtBX{L[dadvWlm[nOlQ]HVuL^P]]ff{uoknpapkp{p}ftiuYvNhxRuNtK_xNmzJTd1P_1Nf3@i;:V=?V2HQ3>]6?S/IP/MN.K['Jd6X`9ZoBbcLxgEcq=_sJlrIiy[fxTs}H~{DoNq{NVqPgRIZff`Ez6sXx[hSmnu`vyiyOoj:zzDhSWtLpx\^Re(uBo?"g9M]{@sCi^zMlSexdPgBc{Ir|1I}H;k4Ia!bogSUwrg]GrOacy͋ZljS=a}^[}uAVv{MgsbNsxG[nkzG}j\]xy皑|YvVZ|]oj9rt2`9qo(6R־Qւm_XhMCBub[zS:XhUrkhNsXFh}W^:^GGz|Qga5K}OuZVgbiriBvVKYyiusraACTZD[VJFvszBYC|j+K҂ԋPPٟ|eďgkcʋSng[{YIMxu;ATBe6ͯvf]cYnH<|XUw|z۹ٰʆyߛ{ohKK{kfz:gP{Us6{Bq|Lhz~xpxafZjm?Zs;mDpLkQjG^T\wEnOnˋڲwOwTg~|vVwOlHsUx=eROxAV}R{feiPwGlVu[tՂvQgb|눱o~mi\Y[TNuN~fdrV|X}l{j}ql}gtkXV_^kduOa7V|@]v]h}V|^dp[lQZyTz`nPhNlXcGa~IjRdKfnvJ8KB~pxLuO\W:Os:bg+~UM]QYT4>;S1$el*y{G?gJk=N}Sl@\}7q:|CπfjC[Zl~\uKp>lJEfCwpvj\]|iwIqhvvUd`rzwvijotcqivw~hrMOg8u\eÄQjaj\{fk@xbu٫ےĄzratyŝLq>|gVYKXXuexnp^eYlcbsQ]~v֏dpDlLmGb_vanQp`t`uEhJqVffxySnYydJWp;hW|do`yznnY`~AeyJsL{b\{QhNoYmO`O]yBYuQ{`vSgLbv9KlGb}MczOa|IVzNYSZW_WiXcSjVidrb^uVjYuUoJrY|`~LkIlVuLQe=`\5Sn1LkLIXEGS9PN5UQ0\P+K[9UT1QP,OY3PX?l`=fi=mjJ{lAi~Ybmh^]OWcCQdE[]9Pc5B_?DR4QR(\a*kn;o6hMaad}VY~JqnGexH^vR]KbzLVBbu>cv=b|G}YVzrPeAoPCTJuW5~RuCWr4r5{Wnbo{|at[]CmBNLuTlbTOEe0\N.Mq>Ri8Qf2bd/_h~ARxCj_;s9e{>v`FQVMW@lf}^Ge˕w[ւ󻴕~MtԃdvOmupdYrCrm2q>Sar_BvSSXW=e³PxܻobpUD;k{IrSvrZQ@klZBn,:TDZZ0yk>?kiyg~l^a3~܏xpdxJӻPnk]YpXo*k_;mI^oWl?{ɓ@q桚rM_S}HX_yjx~Ozhq;Rs2 ^`={ǭd>#djz:ޔٝݙdię^mvfVkוǵijceTcSh1EbC]zGTkGXsRmSmN[{DZzVnXs\~Ueizlwfz^rQjRrZvaqbiBRp=KnP`TUsE[e7]h;_qBfaCLaAfQ:j[3o[5bf6_jDcZ9N_9IQ7gW4mw0UNZdN8_KODNPF:XJ0MW2RW4Wh3Os:Z_:ir.e5czFVuCUpN\iNMqFDeDRZ:V`7^kF`wKjsNgBoU{GlIrSpEmUttQu[|[yXvPqWUL{eтb\a3d"k!}'Uhb;0VZs1PX#fbE_ddswWi:>PKK2[i5v2rv`yuRgUZw[FrJPF4Ym+[v=FsB@S3VZ//Y^kkyWsk4}5~:G|OT|VUpvNÊskĎ]nϏcܙ{ȵOדypkWGUgk,oz6wDMzx{fTgo;ɒnȿS؃H{ÍmTlFso8X]còn]ojt4@UYyWS_Txv԰`qs4z5|cYt\]bVpwǒL{m^Xx]\ksbJ~y\s|ƗvtdVxm~}ĢMcŃfTQ:rl{eGUdPzCg}tڂr\@Ud`nT|dQr@^^/fxR|ħ|ƂnpN^|GZ>VqLƒG;j荇ôePb=krQ`;Ųژئ{hqbi]jzanicyYd[h4`z8pPeNTHf@Kp@oEdRgy`b6PkMyO^ybSrVqMe^i[kLpV\N}>^Pninj}g[bszp}ىfjJmtzppRwGtU}lwm}tWfyTU]|AUHl\jVyNt\s|뛩eUdiiqĨj{_QlEL{LjNcFjGWKb\{dsItPFpS{jɍㅦ]{X}TiNm`~VcpVeauRf@_sQ^vLQi7Ki2Eh@_zFZuAUrQhxVdyHcx=SoPaetpitf{YhT_Xl[dYa\tWrYtF`z9SwL]TTiSTa]X2Yd6d[Boe:zrEayFaeIa];cn;juIxtGh@VQYjUHU2gC:eN'iN7]P1f]@bo9TxAt^B:fMLVIYCP[:HV:Ia9PZ8\W0Yg.MhDZc?hh@a|>fwOxrG_OgU`yJepBevExqKpWz^Wo`WpNul[BPd5b!f#y%Q^bqA+cn'JwJU]0sb E<<\KaX({?{EB]:YI8L9`@.5c/BM8mW"ODJmJYj5X|?[v8Q?pu1rDq>dczL8JnNG8xjdW~\SY`o?^L|J[NMbW}s.G`qrБt{iS\Sk6w9lUnR]^hvTSh]]X_z8cxEc:XH`zS}>Hf]lH~Edxr~ON\\wVR]wU}udPeSH^Io_yb{`KvGkK:TKvbbshIqR`T]thBG{?D_KV#^tMryWkhqOTuhHk[tH|6IgLn:Omylmih~=eNd7MV+rFlLwef֡{b\nw\{zXd\DnytuNfb{<-GТůOŹh˩v\}@xBxJʗySkCLύxXg3Sm(ve~q;_L6B*xv$h>fr5]?BЈjb[zwxgchitŨq}fXøv{eWO}°gհR@P1ih:ԺhҩvjfguTnlPmscUvE^j@n]yLXWyD]tAMw5TpJkUgQp`f\>Rr=iQ{Vtfj\}p~Wzhqf|TtgZlYxdwVrLmD\FhS~uƦӅf^S}U[A_IknVq>TnQ|Ⓒ㓙rYlTadrRxmŅma?jZtK{PqGbL{ccfb{ZxtmPnHgV|g|Μڊf_WwYr_|^^{U`hx^tO\m@YuJ_z:Rq8[xJgLgFeRsQcLZxAXv?RtTfowz~g|ZgTo_t\zSeWrVmSlCSl@\zOkRScUYe8@oC<\EYG*bZ&rmmnAbyI^pI`fChk=j{@pNyVaba}efe@yZvyGUWM^MI`2SV4K}:Te>ci+Yw7[r=OjFXa=\[7mrGy=fHNga^D^]2Zk:dk:ruBoa|Odlx\yhF?~u\1f l x)OV\cZ(jh5HIL\8[`'j}EM8~ax`AY&]H05#Q6Wk*^y>ar9VFdvG]y.o|4U=Ko@zc.f|?pNIdRf?Hp9pi.Y;{whb`][ɔw}mq]iwExxUN\wԊ}ӊwrkd]w\uHTeYnQ@r=lO9}AO[c{4>ubdPmuCDjj~g}=pmUÓ\sҩđ»pԝfʫhhtwuA\ZSUCo[1:V*M<*1$,BN/'tc-zgLLs{r}lJO3HX5XS-En-fc!e[8PSJ˦VTuu]QPrB~2J\p~;HxpD7JtRw=McVZ燓_gVpF~~AJfVuvanXAf,e6u+Fd㐪pst_ct3{21ڿ^x~V傪w܄>poEp4BA\Q>̘_Ãm^FbxCO{7otSfSe|ZxyX`Bqu{RѰvvdjfySxTlT3jzNT[MqzveH^&fr0iі᝟҇hܯQṅʛ쀔MIboKe9pRrWe|ciƨ~pLzDr5Fp!YԍҀcӔع삄gs`tǙj|t炁XTlFW_E[f3EI`oDR|Rcvv[kslKo`WPZp8thbji_7zd}esqew`r`{mgEZd~PawFoT~zpSSh7^pLaZmhd^BxY|vrttGzVPvQ\yHe?c2Sw=^BeH`nOivfCnKWvUe^ntyVXyNp\vuXmghXZhtde|ZHgWxdkZh|vjcvb̓SbH_HiTdL^zJb4cFu{۱օtPxKMnPU}@MvLyU{\{TqVk^Y[{BPlKSs?QrJ]sT[sa]vRizKkq]|CfeÊчəѕshe^aH`xJfj{swVWxI_qD^|ZsOlLcSod~stoTqNlD`vARsPacepqYsblYfUeSdNhVqL\s?`pJv{Id[\{IKwNUcI`d2wr.vtGlyMoKNw]idusVm|Dx_[wfqmwPYqMiTD\X=M^lO[USX*e]3ds=}r?lN^[pf|lPmXc)f"b!r)N^Wpr.j;lW`wTdq4wByWWotungP]]gFyi$Eo_lbeJJGuq6r5HN7X?DQ%QX)Ok5SrErd:qX'nAuJ_NUuDbnJ<epNf>f@mP$=uɂk~{srverqijQVmZIs|ITMwlCzNiJGsNxrclJ\Syw{[;`4deMcKDX{mps’iS{͗uZbЏfpףRmy[d3~moRwFzw3xCTqMta-OuI{AV@OYSr'|~)J=wY&l|HmMauf~V♐^e˓NufE8]źz٩I]eWJyN¥PlliQSndN|Cffc?=ǔt|gx[y2[fq]RɱZhÖLw[LŝqʒNdWbfXlqkNP`+{U=}LoE|^BRA]djزzj_XYZϻߴxnjt[M`{vm`S[kt`ooS}G_qlmќqviXgm6n:hHruyYi}KrhdzRRh2\{&Xj2ep+uLGVkKjLv@yaPg@Om,Fb>_oId_KyP|~cwO_@nb]fvNxMcC[Fo^RZDiKnHvoXyD[NeYmYi[e8Lw?WqD_j|Hv~R[T~jk4Qt0T{=R,Ou/Dg0PqMQjOPm{~WraugrVzivVb}Dk{UZogu_xX|giVbXhlCZs?Pq8[|JjRtOlS}fza{ĉb_}GDh0JwB~Rq2Y5[GnAexGOkRfJYVtebGY:Rk7FfAZTrNrYm~RlsKXnQi>Z|`plbfYkTjRrUe~EZmMl^[XjLbA_v=azU`PizLicyiqnwdx[oZj;FhNoEpO71xWtp>^RLEki.yCTfQbj7udgmSW~MQnDde6rA}OYhRsl[hAtf5iAqvNnLsQ`^lPswa[WhUCe9]!^%ht,vQ_uP`rES0JciMNr_tbyXal`UjIbzdUkZnM_v>R|CSn=:j<5L5GK)I_%MZ5Vl9ssAn3BqaAd`[=hkC@CI^UVUuSMe4r:MYwy5rb?`KXOgv>uPCCTVGg=zi{Ly=WCK+_MhcSt^TSYbKiKMQPT@3}K5lcHgc7M]@dI,n6yAgZ~R|URe׾_贀ʒuܮt햴gÜiǏaljhdž}]|}_aZuLVU`Qft}nf0}Oa][n|meĄvN~68c\۟RzhSmg~AӘ䋮RstтJK~~KTKykeWszmPɧՉn^ye\HLl?yRsրuxP爀rILcιdȈݏڟkcT~5ydKFTyWTw~]ʻdԁʱstu]`FwRmhX|gQfNsI^mAR?Q^*MpkwdlrHcOwQ|Ys[oQvJi~FzJo}A\{Ig{>WzLeKYwO}Yj7Nr1Oj/Qn3W}G`K^_vqiige{MhEuG|KsOtRDsNx_xZjLh]xijR\yFkRnJwTZVwDl9Zp:\nD^w;SyUjbn^ZQdCX~PnOhQkPlNe}VyႫZqBiNpTh}FXmEk}Zz{EMY?AXHViL[x]ndyD[v3PoIc~Zj|w`lCb|PdJcwG^InvAUxL`yTvFkJFjIa]Glk:DhYW`Hw\\gJcoJpx[kVUmijhlZ{Z]_etfwGcnChs?uxGvBMvunTrgpJrBɠVuc~nav>rRTUoh]_ulG`z7pr@zvVtdt`Gz\zmrjOolG^vOF{MHdKbU-^*=[6]`!b"|m+EfM:XN:-`Z%e`OUqtrfdg~yHOfvnq`Ye\vMcq'S~J\d?av+XnvD}XVυԁOLmVpPYo-}Se?xR}wWYaXRg׌~Od\GdkrV߱cazzRZlXW8h{+l1p;Apr6wU^^^{NyKTyKcy?w<`r@`xB\AHwJPoJQ}GdvZtXqLZ{xXPN4OP-dg+g-fyLvr;7lK]oKgqFaON}qQwQTXf{Bw/^\ȁbycvQcFgoCnIX֢eb}twµzԑaEd}6wwJyՓzHsĝeNqeQβY{lՓw{Xc~1y6\`nfFWԅ_XvS\ibV~FiiSV}HYjdaVo}]ƑxXqNeFhѷnkUˆi}d}ꏭt֗]q=|E~B\Y|PsLGgRsdsFsOj\xNqKe=e=jD_JfMtYvzHYmB]`VvAGiD[65^2X%Q#Q\%jd5uvF`aGxp]&?ŢYwzahˈfkNUVZ'po&;vaY~DoXg^uQyI|jLt]X~PTwOYac\Cw=Rcc+r,CmݘzkYzHjKpTXo~fPZX|avesGLxWMr]zzț7cXZuPt~X{Gkwey]HXrxxkskTţ\ߧb~lW{h@M`fJM_L-uDL]G|.Uzf~9ZimY=}A_z_GR0]*LI"yB̉}]luRb{~{Jn}\uq[]]LYp׋艛xcVPldwZbCVd:\i1Wsu@skX؊Čƃ̂uzz|W}o{hzPkLX{?c=hOh_n]ntWJe?rGGr4Om>^xJvdldZF_-E`#=L3Ga.Mb9Vl8Jo(A\0Nj;^{CYUckn_hyO`a‚]wn~xL_DMhAx_]Ehz@nFrojGyMuVqi[n^kr[`LkIaI[ZzFeOnT[}3Ks=]wLexYg=Td:`m>{w@NeopniLLdfopX\q?ntCOPkXYi^{fFxy4]PVl@c.9m:Z nR#Az3C]8A`(GS.8g=9M/G94cQO+jo"i@ceovy]o~zJmjQy3Fhqx\QlZlRpq6Elfyoro}XWupgro?]e}atQ]onjmHjp:qzGoqSE~I}YϢXhϦloBΡb؃dFPۜfKxWtt1Mm>̣gm[ssF|ggoFg\YE)\[=PO[o_qRnBAAk:> q_!\ջ`Zf?Žm~v^ݺ宻gMt9hA\:Ba_EꂂsT}ӐaSlOZ`̯cO]ʼݾqhUY=ëVrY~Q}\~Rb9Vu#Rr5V{=pntLd{HqNMlWjsgfzamsVYW2dzNsdÅxox{ƒDžuigYyI]I`xEVzJ[Qy{ic:XrM[jBYf<^w6f}Ck{G[qcKo~JuKsV}Q]KGl]NY<_^*et0WF]xGdt;iD_zGcs=jy[lJkfJ}YU}]}GWpKWv?csAlXWsYJgZ^YEVk9mDjQec1Uz0MwHbL`o=Ml/>V4Ea3[jJmyNYy59Y$AT8K_5E`/Lf*K_*V.b3f8TwJvsZDs'La.\vAonnrVaYl_nGTnns‹nt}|josen\rfykq`ziur}||m}JeKqJszȂcd8JU)BW5Sk@cIg=h{DX}3OmI[xF{V]Uy[Wq{YupBvsDno`gxiapk}ZqFv`RYMdV2Z:YsV`iF_vCttGfyJSn`cIfk7dx4W~ChrNev>kLsVnYVvNxYl{Rjbl|TtSgXWY_uOmy;y{J$?M8RS4ii&r;ENRb7ba)Yw7Gs1A^)NX$Tb%Ol*RT,Wg)ksK;jcGX`5OkYFliPVzUIfaDKoպܲr{Njgi]xPqI@t`[u^lN{{CAOOaWtykfip\SYmy|}oKtY{=CdjtQ|hmYWxdN`oغzuwLӦ\eofnYS\dsfjom~KgnNJ{ptygwKf{OK|xjP~uinjZnu^GmN|yRz]zmdrolr]ac~x[]YrfUgMYnBTr6Om4[nTZVoPTtGe~SjU|k|UczhāRstL}SGjmBbqS_sKctW{]kszmrM[zSwohn~[wbf]wniXjnZiUus}pt^mdnh|bn@H_2A`CUt\l[r[hetukNb~SeXl^hQj\zkdRd[phrmbRdzWIYcKjC{J^qmanjduOtMh]Coq3aA]s[ggBhs8iyH}zL~MqRdE\C\}Lcx?n:~O[q^riqN}QtLr|Y}MM\PY}aicDz{CBXrksulr5RHrr7qw3ES_kpzKLq39k20D31?/^Adv$E=VN4Ch$U\>8h7?G/eOr*^jzJa_[.o,6iV\{_dn?En=}PL~`h]|}sHGjMVɣMWbAgKlZCzdQLnnzKuqUZCl|dɀq\}GWUy_TȥJrLcWth{TL`XKMJ@tِlM|?sA;RfXpLuRbaÊŷowuϺwDjhIQzBeчfcvFBc}VEeږlİwn׀mêϿ{OfaU{T]u6y5ZGkZʀVˣSST]́ht_QT»okgNrnYdWw}u0cHoskZDbYRBsS{Ųç[^q1eBwsbx_Q2GV aZ3_9R2j\Il;Vb^[~ph\dnQnwUltYIi=:O&DK5yayVgNpDlp>x[txiolTxԦhҮhXLvJQf1v{JgAsHYjD{Nsvf^|DUuEgNh4Wi:NV3Rb2Nf4yZrcgnSwU`:Mx.Rw;`FY;e}Jw^w_X}7eQUXACfUzkQ\vSu`pXzUSz@Zt]IeSs=hD\\}pNCU~qtok^mfc^[zURpIn_:2jWh}btvNHkafyfvuRyRyM~QO~JLq[ijl[gazuPK~QzXmOtS]IJzT]b3ww:tz=R^ȐnjkVjYh9rpGB`ftIsCAK$YH<<"Kv-\SGCS_,Iy3I]4KR(d^&Pn/X]?eqWz`HT?NNCJ3lZBrgCNAZxkvPILjusRbe]e~In=Kjxg^KumTUdązvz̓jƙwleK{?wVj|Qo`mb]Ε;iە[e^l/t/qhdl_j{xztp]noKxAWkrK?q뭎f\}uSrNTl8io;Wi8ih2`vc{`JK`uR[syJ?}N_Ɉmvbmm'Sʵ{rfݸ^Kjekjjie?G]yr^`v`wQ~ŔduDR᪇zԁDҙ[̠u@yGLף_pqܬK똗JNXڒ}:xܝd~|SOunڢjҦ`}a_U=bkJjpWfeTxs:K)ybvMd}Visdoa}n]=tL`=`VeV\zMg|9`Rhjw[rimaSzGTr\cRZ}OEu85R6[v[Np\xiYZXxySvJec=WiENcFYqgYs_b~kqXayavXt]wW^{QwahgcYHnKhOjPiHy]oK^S`Wi\imNhxU^wے؃hlNXzTzdlMeduRM^4BqDZOvRm@Xq;GoEfgkv{gpObsZgpl\lTm\}VdtPmftosxcyO|SfeLf|CVlAejT[u8jo;fFn\VYUS}umhNSv:Qx6i-2s`vjoa~YlrcWyjKsv>yO{VYXm^mc\RoR}wLtPuThJhKd~Qg@qFvEuFp{I|ESpwmr`qq]yDrJ?xdarZlLvt>{H|\`Hs?{OQmUOcIW]yo.-LvUKPGk?A[g;Gf$Y+YB/kaF<(lIq3hml|hy|axzqttkUkm[kKRtown_KxpA{EocGQb}SxWrMZt`e`9`m|R]Gh<|OJvwYxzN@ځi~¾p󍁫Ɂ^pvHЄuw_hj}y[T\ߌҨ_̭Ѥӡt^sRCVGW0^e }{;V~TUes_zWiOOouD÷|v_w=Bfzf\0^k1o9uFI}L{5ӪSΗttc8[LUȼUxThWm،}zJ풭t]М}م͊eͣRj\{x/qOTqr{JUwycj-ۺ=[Wq`ʂ̓hcŬ[„Љl^ܱНjSg8bN언׍ԙck}k۞i\exy}]vDvF|HSNUԊXrwK7knVP]~eyŘݍRgs_aeLGNfkoj|Uwmq;QʃyuGN`2WC"mDL߱՘L|֌x^ƒnvvkadթo롟nʋĉwxNpIWwI;j>V~_vguxR{cvTc㰰˄cKb~?cHkɉɂw_Fr@YlS{P[r/d8f>^xNl7DV3YU9_g'Yy8|6ZK5jsIH(TQ%`U0r<`xUWnfjVHc_p|ZxU};mz;q>P]ua^Rgc;u2igGVf^ubz<[zG`v5|t4pBlqLSfbe_TXukYrCaEx}PYwsO…֤ʙ桕i}l}X]lwsvZyxTubpZ3pm7jxQo9^ƫ[{qmRu6~H޹LedsV3utdZqq]d֪tƀmZl|RtaK6U]IWT}QrFtPC>iQWGcM왬ߟQaðpsWުqʺѽUc/y@QTe|Ov@nz]Wq8a_.gvBGb-]3~{Df[e_q5،ZuBLG+<]|f_|O\YuJqD_>pacIaϢغ십ڔnqvjasIoRa^WeE~NzifÑUju]tP3a%ZbFSr@jY||wn]vRj{aszklCvމStdsjfZjIvCqAgM}ZnjNb|iypi~sh[pco\_hIhPlUgRhTby=Tz5[p?sWme}d~UZzApMfOzwszfX~HiwLx~]ytqFt?Rj4H^3=U'7J)D_5;OFavf|gq?Kn8FoGwqx]X{Fdagja~rrGiMMZj,>^*F_;Qj>LhFhRai8LjLgUjVgcqtwhxSu]j`lS_wWnUuLt~HnM|zt]woҵ옡ezPUH]~NsnvfrMmMbfqk}mrVcDOoDLgfWonp~rV^yG_vHiOsRcuX^al]ib~suzv`^MxEN_=H_0dX6t{Ck_Q[^n`m>>lTe_XRYwTmmAO9TuCVi=U_;To<_J~Rlpc~pNYbWV{l5q>coFQoBfX@nGaZvpB=Zsil];Nrjbw^[{[vgeN[ivo\xHP~gDGRP^fxoqHkTjwJz:J}L|WDuȂvȈ}WnZ\pl~~U{kOO~Js?~Kc|Kdhq\\BDmS-f>tMDZcgSnJWԳ\{Tkbr*M|D{]BƶRq|_|XWzЈwjneBytnCyxH]yIunKHtfMQkk:_7^F`ά޿vyrpvjmjTMyčyxwNNQpm6;zqpazQ^jf{OP\ƋLe\]TUOrcHug^ქoKǹ<뵗Z݌|׫ljhRijz_?i`ƥFΝeMtNggtEmJfyazCnާo|iJؾugPJcuh}I]mA\eOYg=y6tPnj~LYxS?Kyz`g;NT'ni7sLY1Xb:g7oSǷ򬖣⽬qXrR{TdnOpzmWt\FmEpYfA]z0O(@X*byE\v2_6Gk,Be.?J5}`yqȳlT|ajrWpȅ{}luy`hQZzAcWymtNlcdT_GcEkUxTmPrX|YS|UsJy\k`YqNtf\quhfyY\FCj?Z>\JxSpYtamc~z|OOhHnYfBe>Kv,Tm4_:@\-2J2AXKlzNcwDctIc|MPhCGaBczfsw~O_xdm\l};Tc2Mf,He5Qj3OkJhWjBcOdTs_bns|͉obk|ZdQva{bYY{Ӌ́Z~Qtc{m~]YsDoVat[vqR\8[]h{w{srY^y>Tz@VuOYpitry`s]Mor?`lUcNprKs{_peuktvtuwlx^RuxWX}LYkDWo?ta;i|{F|jD|C^Z}_taueCyCBKLkYrg9w@x\IdgxsXcpznvYeluiqʈNJeqyymxibSutIlGAv`35[~~|ovwtIrIWaoq_}KzAUMkY{KJvcmJGUoׂjYy7^A}JBj|yfk{B`Cypm}X}UzonWXKiz38}܄zԛsmghHS~|hW[pLvanuotS^=Ϗ~YekcGu8Cl]n?hY{E`F^8Y}6{Qsf_B^wRмvc}[ᢔmhkDK_;\w5_u?ip}e~dtqmIGg7M_D}p[sQiZYeJvRzfd\qcGZwN[pZ{x^sV`7Gc2LaKykb`pFJ_0HY,Nb(Cd4I^0A]0E\5h}Jey8XeBdvRoOgLuqrWIc1?V(Ic7Zw;atM|XRU~[emctSt\gmn~fphs_l\|cmVjatwwvcwN^BZ}TqQhNj]`~qku]nPqJj_{jn|NYvW]eyg|Dž}tgfWvPowYz}Z{Olk@g`CMfbyohXzqSjp`w]}TxPUw>kgDs@hTY}[asYtmIpUs]W]RwSbrEQy5WqDMr`R_vNoqGdEAqVQ92g`,QwZjrPP~pycsNLHX|PUS}=?U[Qct񑩦jθf𩑄EպW}qzsbʉwRX_˅fasyqqL|^lƣޔQٮQ^JRr)R`<_p5?M;^:;5}урXsP];ca-o7pBO|rqeB{>xE>Шew|I9V8}pscoTFͽ|lڤ}q@`Ir=hbˉ[~biJs}5HP{uÌ폔zmLqeVtKEo-HQdxqz^)csIX5Jq:fcbju[|gbve\[|F{Hk=]eAyYoQexPcsV}b|Xr^[zZ_hbvM|\΅vxG]~AjOo~[tpghhU~HuD[Sj~habz:Nc1XuCpNI[i7FQ.O^JkxVYeGhVP^xTyc|iqQpE]>ido]xqSjSqru|iZtSmUw_c_{pqiWau⟲hz`avWYzt|]aOkdˎ]^sE`sayhydt^ZlAQwgz}؃mhmڐwiV]B^]8FVHIZQ^bJh^?ebEh}UqzTo\[{>]Njd[v\|Zi?vBXmSi^jIpy7Z>ZlO@e9?U/PR%Ga)G[k;eZtKfY]fu{Gy9Bz^qcMTnyФUxo@4Qbi|xsj}nlt?|4qNwFyMmSnOlbt|YgeՙhNLpUm}NlL_{Oq?~FTOUvpcGvgRWNQV\vWRSqŽq|▄|wQif\BiܑÉʊʊzxtljmjRfyt}\mvZ[[oMPlK}O@Ț]|hliaKg{/^|4=bFI7#>"BmlhbkPoF>J|t|sln^@Jc[el`5w?RjVqQNώhxhZLJƷpoVs̑ZdZv?6rM}6{}IxXeetǠV_퓿xjOlPPfc_Q[.U5$SKekNk7{=Xf39yuuP~MXm~r|ykaπ߈|csъowTtcWmte[WlcRy]ޏ\Yvf~[t=i^izedisv_{]cauS]ngaiOYq@^MX2w.g|/LwOw{W^O痲äw=ΙyvPsюsaJbE~r\gr̵i؇Tw+]LfLӡmx|DӃpKj]6Ho@wgE|IȽJMgﮑVn|E[kBxhǁmeYUYL꟬d`=VSiXpRy}upV{U8RS]F{oI^rWW}CibxnzhlWZxTe\]zAG[,6I'CS3L^DyzTutZkfDTa8Vk9]iNk~Hraejl|v}bokuÔ̅bkKSn:Qv\wd~`|a}m_uie{\zZmQ\cPcpUw~PfK[^i{y\e`lmzs|YkTbi}olxX`}_onviYyotzĂiVkZshu~ƅc{cgdzHiGdtNsKw}DpyY|`U|esmPjdphbbme`wcpDKh_p^i}?cDVDQ|RAlBKVuixqzAQv|=DzP۱d{qwY{OLJqz\l>YTasSY;n{Ya\lzdqSukIr7b|9ap={5|LnO|Lrk?pt9P^CxCpmcncwznZ]z7{qHa|v뀭MLߘޏvӨӜ؁Ҥ֕iy̅yXtJFTLSڕfi?LkqՑŴrh`7pU䫓\{h֕ohoގ♊gLRU=Yvُ|\sHнbzf_njFokkRltf{KuJZ~:Xi=|qs|bkTpMiтmLPt:`|8iNyxfjyYeOW|DL\'_}1Y2W5iAnVkJyJFQ4uQtPRm;_sqlnHTxbV\DQo2UsqLYiC^rLVlEn\IqQw`jQR~@UOzVsGdj<`j?bpDn[sMv|k~}|ln_o{_sYrcuSdNgessmopkp`oWkcbI[EbD[GmOVNh\{_utsbw]{rPbvDNqQfXdlftw|Vny{nwcsr~Z@a~/Ag2<]@JpRY\VubolYy]z`ckqhtam}XsTQbbr[aqR{QfNe|ZlM[sCm~MkBnIYwVYZE{KR[;Zl1Z~7a{EKLNX@=N.t=댗ÈZbש{tfo͕}}xwUܖ]ujV˱wTk|8fW]v8s:Wt@m^pKrN҃}mfhϨlOf댵jchƆIpq}nmg8qo2nC_œrZHovcܩMp˕˴˷~`d?ovA֌e}[X`T0wJ҅f~zdcuRiMxi^atG^q6xі~SxnvD]9avRuHawSipN^xGqgCUvΈ׈Zwm`BYm8Jh*7]!F_:[k9Ro2lS|ԇّs]iXy~Lju9F^.>T@bXRwؿ㸀mrvsllQudÀhzktDd}JqJr]LawF^oD\v@ezUrm{_{łj}rtu[i|X~v}{·tfydxZ_;]~Jt~{}xw~]u\~sڔ}xsiTTyaoYsQqbgظkgFHmRi\|on{NkZq[p`kCTmHZq愄QWl6IfIKef~˚ty]p}RxhA5@./IGNlAY@ZVe{G]o7Yr;T}JaPs]tjW|KjSyQn}Hce5^Y3iZ0\rFcvDcLjiRuCHRjgy}\:[qGhnEh{BiSG]?`UNP6^Y3Xh4gmAtw@rNtVq]w`pYsd^`vbnq_dTftRbrFjNsFDbLbyNLvJdrJxDSsfdUf|KdUq}Nzm9yuMgZLtH\zwsOi@Q]M|Ijp%v3VcO{cKs4Mj&G\'Ne*`k2WnCR:3pʺeօȝqcpa|Xtu1\ZzZV@YhD}r;K_|yej`|wlx[{i[eęaz~ZotiEw:qWalʀيҸuh]`Jy~DDn]^raw^hu6}O~W}O`QτjƎψŰ쉢mޜsE^FH\)}1{Əʗh[CY0](dV|L~ϜeLj~Tƍzrta^܏sqPh헄Ѐy8|=xtf-xUQ[pHxfI{fY|ꇞHdL՞ЫgDwO_xpӉĐmeXi=xgz[DDU"`7‰[f;0WzmzFqMT>KvӤ|Q_F·zP}àΪ㚒KUYxvyz\{=nDrUtejqArCYzarZp|\{լ_Ueyariiuuv\\jR_nNIh7mlQ_}gLu"F^0=W+?Q!9PPh>gRath^t]Fl?Zu.Z8pOHBe.dӫv;Zw=Ia2kR}mKmxOv̲¹䅊]oCW_oAa~B[n[o^sliSi~rc\g^nw}aZsYjpxtNjwIM`gd}jtfvewnwh]|HZmNexP^xFbCuYpkmkȚݶяbsQuST{W[v^d{ZjNn`aUk:Io;Zl鑡_xA^nALlW_vndn:BdCdUd7Fi:NqIm]qTswg|XoGbC^=WE^[qg|[gXeKh{GcrC\[=ej?rs=rc4g]1ig:`=apD|D͏FV[UT{=Yl=Ws=ay=f=RSNuDNX;V^.Tf2Ve9rm5iCaR`|ZqzPoMcbc`~Qdkpc|\aafdeyEXCf~EqHawBsvRa=gIv|BIy\rRuG^~OdtDme2uu9{JzUxU`jsuW^BSuQNh5Z`+\r+:~D4e@AM-fZou sD`QrHl;Kx{׆nyy_yQtLQ?jLU@Wq<g2yOvRdXL\b{ˇxk^u^X]`~V|sfdQRg~xvsnzuodjVAzmps^On5\q:}m=RUpR׈S{xJزWw]ɣynviTSqBy\3A“vƃwtXiyTY¤]W{k]QTRiHr8Gb%MV#cf6e@Fu~hŏÂ}cMowTlWdf]xIfMLϛZrjGjx}zl^xJL?Sf[sd@g~6GTcjkx]>zQO\wٳETKurH~m\a{iybJj]k_x9Um-My1vcAKdUb#2Mnl.ZlΝS^Nf!vIeαƵ}쑍wjFh+cuI}ccƖ{ģщiYehGpIno5`EAmQy`7oj>򋗹plHe^Mti[Y}*r@ZzGXwLgBbpn?iJ6V#e9BonKnV+WrCzV~]uqQSgشs_׺}ͺֺ⋳Z\0n?{`fՈNJҰݪϩϟpIjMvEǼׅarKk=Xo9FJ+eh,hlB[rCuvAyG^h~LVwgiiHvIbd^q7rqCpPrbx;U6.E3NHXb`FheNT};:d"8[!6Z=Q"K]1I\4Wv:d>Mf8]nXlu]sJ_\΀Ӈq[Z|3`;[1c@nEhD_Bo`xa~RqЏ|QkA_wAPwA[KkFdUeEMjA^vqhpn`mRsdiRtS~Wo]ah[gY]g\qcfAPqHkVp̍ul[i~]xZvr~̍o}\azRixYZ~QyMgy@pYz֠|͈Օfr`|Ncz9QoHdXtWkSeGbI_I^dzlrgvQftH^}S_sjM|U}nn~fs\Zofl_^\]zSZ}]n[r?X>[ELg3Ce.@b7BiO[KYxNiJh~Eg~@ca5`_:Qd4_e.jo8h}>ru?ztt?o}VaGl^w@yQyKttE|{DotU_jLZr>_}6vxGdFPuURjFFuFAlI2]COG)Pt4u^Ee;n>V[(ai#tH~\eXQ[ttjTfOz:fY|CyRgVMMg9X;]d]r?uo?„Gpl͆[zepYyD4gW]xVBN{8cEU7May_tQޠXltcz<~Sc}blCcjanqeNY<{vۃmυku0VT};Q֝昭Uv?_ПǶ\l[uL|X}L}^|tPK̓yGy:qDTmIiz3v~;]k8Yn*xu2jxws~kXT]o0]r(rv<[twKqrGNg[wVYa<~HeFoZi]Tel`dLsXo~Jy;gpw厪ixcMIū]xPaZPgl@z޺k|l~Ņi|LY[xV|[XeDXۀϬᄐ╙ofL=T^Xk>~d?X|P䞑T>k&7e+9W(k\dSeed,7T To-Rz4cDbQ_{@pzhwyztjM|qޑiPe>qOeGW~9]Esaeo怦geazqeoCV:fGrW~^fIQb7gsJ_OXLYrYwcqZv}]f[jI]O{zu}hZjSuVdM^IilښӄTaEf|MwlބӄnrW|ghoPqLftDbRoa}x{wx}Yh|JuSy`tRlJqDVsIcHf}QrUaco`{``xCFXIjn}uaTzFmiubgGQuESuGOhPTxSXpOPwOkI\~JbUqCWp1?b7NtSqXk[cZk_~Hhr?f|KRQRk=Rc7Tn6Pd7gl8Ek7G_Q[`8qe0p9ZH`{Ph|JjEWxUOwP[e8^]4bf0ckATtQLjDZ_1il4oAoFRjRhWM_Ih`HpAsWqinUmQeZnwOuBRwXo[x}SzGsW|\G}Mo{KpzJbN]vNoD?r[KaOCl+]E@=YNa"Z}ZsWwMnj3iCzS\tazrGqDljriyXrX?ghuRQzuvqxHStefRa{>|COWckg\BLT|mrerj}wevuciPtOsIP~8=xFQ](ro1C|`VkLeJlq7M˗|~vbguiyNo\x]lgc?{JzQr]oUrlw}pkZHtuptKTxfKhB`@6P/SW$^,r6eIzJaj?b}~kkxoQlTbog{`>d~xyc}`NyːaeqrpČybrcqRMwkwhTi`˂械ݔMrAi/Na"ux+\Hwn3lZ9O)lL=shI~eGDE-QL1[|Li^}̟挣m_P]M~䚼ٹamrat]Un(uNnXS_>j݇gbèjqz\Tdne1@>G)^\L|kwӌƮkeXag3bp8[z;pRQBi'\p3dp9R[-0B ?P2gv٘cf7]HbPfܞِyZZ|WcwY{msQtLlL|MoE\xK\wF\~MTserzZhQnY΃nfjMkYsX`}evUYu:U[5VFcGgMj^t|Ք؊euDfIbLdkyq̓if[uemXnNz]_p|dmrzsq]whōdx{@Un4So@`VZ]rUoYiNZ}/.L3HgOffmoXq=X=TQeWecmZwMltCULXAJoNbMYyRefyTj{J^YjapV\~HWvSjim\hW|a`iY}MflEg@aKRzKE{DPY9L_.t^.ju-Z~Qb{Si{?awG[xMatDpjsksu7]jWm^W@{f2lKNfbO@cRotMLωeySV~kTC|cqETIjwvNJccxnZsO~Tdihb]j_k:qFYZu9{\3JVzUTw@WsDiw2y/F}i^sQ`lkTэkvwSYx:q5\kmvqriRy}sJen\ue[xORPJo6{akZiHg~Zl{O|Qh[@}V5K';IpU!~Vtg^hQdHi4yGnxNDy~Y`~x5WROvBr9Jn}eeLWlQyoPyL׵V౜npv֑ywodrVGۛn{LVuFe<;t_JQz;W}-]'x2DHiQz7vRmM>Od\RQTsrO`۳g|ft:wPoLosv^mqKqHTZp4蔨܅dwxhQdOM_KYR,wOtDMvfTb_nCXo%-qdqHBnslY٘mukBcC`|3AVRKUvP[ubl@Tl0ap9]{?@X(SUn*FbeGxqF~m;Zh|ʰڌvbT>fs^BEd,{GbŒʨrJtp\c\bG,ROoQWu0pT[ͬtJшس޹j|p{\wMؑ\ܟԑ޸ጝwV{5lnBGb.9E!^jCw\S5Y_x_ojLr5j]=xOIb"c|-b3Z1]{h@HM5=3::\lXsryvմir}UoyL_}mSpJ_:\}Z|rߵmlPj㊷|n[Qm:msRVwW~fuzSa54X4VLnTnW|yvkpeϊۂwwyTrKfehHUzASnIhHi~K]OmfyxuLmSn_w]oe֊ƀbyScrcxv^}qqdvPkvB\sY|lkouyLaq=Yx=X{H^h{bt`v`NAsBdHfmZt4U|ZMof?{i>qoLbtODy]C]FfR2fb0wGhR\yk^sGcqJlxFUOVqMah9ifZJj~3eDgUUxJLq9rj.WN{i;NQFb=[.{ko|zׇsytSmcXU[j}OnwxbrSQgwhTX@^W.?q{JPzmXsIeBCiKc=\m8GpJLfUJsgEiBzbmsDtME[{sd:]Iaa轎rUXTVvZrVJI[,Mf>HS)TZ+ekOj~W^xW{o},sdU|:g}h\ERu=Sw'Kx"kj3K}E[kCe2y>^{Fif7uSyYTJfzwӧ͟q|t{ush~|Fi@fe3Z[-iBo~6t0r቞aͩvͯ}X_Ug;dCohqKK7a>Ff1[c5ad7k|Ejx8LW-V^Hʳ˜ϣǢ˦oSe:Pi+AO*`4H&}Y}uyzƍ۫ƿwßOK64C6FG,O2iJwcMh՟ׅt|xrvrxekr}mLJ̟}JHy,?q>6c=MJ&FN!aP*Eh/I[>@\2GT1fM2\d+_^@aeDWbFae>\h6ac7ZJgo@\o=N_BYR7Eb6ElDfS2k^%q?~CfcvvTgNzyQuI|YFsFnPI{ngq\TnAck@m0qOlV`|T\nUsIRmvugjL5p͒XkoMdCa[ZFjg?h{?|cp_{cy@W^kdjDsLUZrXgXiHlQR_InFsQ1py5oKjGpFj@WrGia>?q]X^sv:c=]}Mhd-|5WbVOX_EW[b3EOKaohcmx7J΀ƬzPwjA@aql=^FN+OnBbp-au7z:ZMgwB{fVz%v{LYyfe%OVRpjuBZ4[{r5Xp0RgeI@sPׁoeGx]l;kHEv>s\zA{mVQ^;Qoe΂ǒe|jÆ΄҇cb?RjHZ!{7LuɁǏӒĕ~~OӞFSqLgePhulfQGW*_w4i9\\<\8/6A>-Z\*Pb3glF^tHnGKg?dLK|ʂcpCߗ}mMo|Qw:i;t`:rm܎ۆބ|p䍫놞|qahnşXUfl8h}PcZUxPRe=Bm58^6?T.IL"]T+Hg.MW=Sb2_^3\V1IV9dJ:fm6nrLo~N_uFge=hw8pd<_e>DlBNQ4]b/]o6aq?[d6qc=vn\_LVr5Ye4qp;?n}^N\y=]4k8OyEGj<]f0fs9sq4~ClSqXêe`}yh?__Tj;lubpH^]A{9yZ~NuKIuuzIMjF@ƈ[:k{'uNSp[tanaoǏg\LR|.VR@oFCzeaˊO`ƙYmskxZjNL~r}rKjj_bH`&_g.hq9^9FHRf@OGKWa:Ut;X[+HmGfIc5j(\ֈa[j{.IeEtd>`pl[Z֬ӄOwH{{8pNx7h{.d@cs?Qw>ER(`kJpԍpd[EL+QY,oz=ꔱfkp썳N|E{@aTxQԒgMbÊԖi雙uyHyb~i푞򬌾R^v3k3lo?MRn~pywdv{xVXo0\x6Ug@tౘ͑w_tK}۸`ppCk’Yr饿kW~ŅWi(^[Bo\T{㗔u{r{zplszЁ]7Go'bDzJ]:pBl?i]^vbujiuv6t7nOTKfTY8W`1KW2Mk*cl=`u@:YWKMtVX}?l.dGz~LeVFo9Zh+ʍ4v}_~W_|wܠgWf|HϔQ|ͅZmpkGsJx_HvC^c)ThTVJ6={d{Xj}t}\ocfpCF{r_wGJ`;_v'zCPqooLxLBB$;P3IB@M OR"r3N};UzWa~4veg|pxeErF~TW5qm=CvskJwOukPpOqNimByzk:Bk3cLjuf~VfK]gUaz3~W~dw_csW`rKzr7E`YL8}bG~r}Zˇmlcā]uѸtW}th{VάsrgiRkgZH]0?T,T\8Tg3bp5型`lVЯ~z[Pr8zx`}h}__^XNJʠқуՌV^h_7l_3so>alFx=kLevKqtOz@WzӛhskNxTrHh_NcAgCPH-nM&z1~hrztWQQ_i;l}:EFz[dkir\MGUu@{t8YJUZU]LoWAXaSvRmRq~B^D``TSg{cfNGplzgkQWDc@]\)~>kIzq\weTzfIV\NagMyEdw+|k2Jj\n|GS[S^M~6rSְEdlYLS|FXe0t|ChJ}:XwhnQ{Nil+nKh=xKS{ieThDo_7\h5x~~CZyJZAb5Jovm^Ot6ҁmܒuIogwGQneXV`reOMhq1\uAl+AH"9S"RJeOLVc.sj4\EUr(|ErcLqQ{CZh1de3fLOS\\xv=N|ka@nbuSlZVw:Gm^|`w|lgVmd?aP*Z!7'&DZ(ng2~BG喦s{v_5A oa4hST;Cuz@`B[Tcf˦}_rG^wF~USktTuZßwӏu‹exNEm'WR/e:}XNb*Zp-|X̴٦^Et*RR+Ns(@F2xJ@?wꅉR|>^ԅh_a^=cTXkzaJph8qq<`qeHdCynjgYnZ|hcaiZ옴qtPc6Nm>`qMHb8SlT^]wDc~=\zEuKsQnTj_lyGeGaLoddatuN|UtZmMgFjSupnnwMgN}d~djJJh-F^/NjDk\o]]fTu[oRpIjWea|Xsmqw^rNcV_TucaoqadVl}^|MWz>RuRuVkC^|Ad_|_~GUVvowwb{~YTm`spcЕd]nKuOpUoSj|LYlKR|vvufhfjlopqbxMlE\x>Qu?QqKSmrzlNF!WQ!re/f?JW>nJNO9ec&ftDoyAh|CZyObOdIh|Nl@_[i`dEfUqLhVPgO[Pb[1Vz?PdIQS3RW`djQuibd{B`dƃ\^oĬꞫ⤁ah}?`ߊa𝈬Eݔtd_kIqP[qn6iCQ͝\焄kzq_rbaF]]Ʌ~K;^a*nm:|[>yBLn2bQ(zpSub@ED6B5X8~cPi=bZ8gjANi7~q:ed8n]bVlf4blDhyGtBpօĶÉ홢eWmlJ0~EPQe|pRK*eOZ^8P5(P=/jS>XdxCOP#N_$`y>Mn4x|Oc|K\sGj{S~pR۵r}YJƶsEzToTXL=^-=R"D["_pMsr:qDVtp2s9Fl؆dK|6~caWTzjT§͠^}~O~vA}Uwv˙ړPq3XS&Yf?扠~|drbkHJTAmhhfidjd`^X[2O[4W5El'TgHgp0Xi,WuCqXiqXk}Tmr?Nf6SqX}ipmUsOxQwVX0Gy4RrRz艡⃍thViR_~Df}N]BLp,Dk6\yDnS}WqJQtDs}㓝tfp`vuauS^glbcSlfw}lo^oXcr?MgUbi~Dq{:c]hj{\k[u\ajmhefz`|YkznYyvȶh^^xVoOjzRixF_Mci}xy~mxmvfuropwvxbbObOdCT}I^xƄxRzC]pEn:aFN`HpQ\Z5\x1Xo>Q]?P_@m\@h@SWeuL|x;mZ^rZ~X[o?]w@bx=eLqLwBZ΍ʕnmpshyTkCL}Xf^_ydVdDOOHiF7mT*moMobEX]DUK=WJ&kN*vQ/V^DUM6R/a4nWkNUtSrg\yZG^y2u0^Pm|IfOQE׵~nUd~tbsKp`\bUQ8_uSP>izlngm۝?TvʢsphYmlsal\=Jh}_D7zEBurj`FՁӛiљjnrpBCshdYCmKS߉ҬyuhgkdwplCvorW=ZH~^wT2a?Dw҂YT[_m|sYuX^XHop6q;nN~Ju`vRrH{BsLoEoduST_KtAQd’|f}{ζ[򺓷Y\[ٽYtmuy{_zR^WerHozKٕu}`WNOzVebA~g̠jpՄNtsXYvZu3ePx;q@]CBjYPn+fDzD_lRO\mVzNzAqAfr8}:^{e?vH;!hu2U쟜򤂰BiMST5JG'T@!^0odpHGW*Qa-MN-XT+td5}bCrghpEg{>|HGjo\uyYlp鸁fpzv|o܅|ػ榇yTm=z]:WzRǞoqNoɦŐۓuSm@e\9ad.KB_B}nFaC4iM:zY}gW79Frj;pMZ?t'(F;4"ʠtwMn>`qcgT]TN¡׎exRwaczKTԑcbz1p=w_gyepTbYwqw|Cn~Q;бZ}Szs}dNt3cBWsPzN`g旣ʀ_yGQBWEKw1EgCZ{B`Ej?tJc|xkQcESzUUz4f+Ss9ft4`Mu<}Mtdyp滑~نkXjDUm:\f3[p5:U_}UTZW^g`OVPƤתֆoRƗsfTԡnXJgdtnn{lcidfSdLGc\r@N<3Z:XW"mItp„mLB{r~kNzX[WvVFtXsLo6=Lay[zMG}o]lsgdbD񑢻z|1MGXQnjڌbիliu`jlCpCX],p;Vq8n:{y|GioS׮zoRYr][;gG^|ףhj?ĸjI|iIg+eS+cw}MovWhhyٲzzZ]BsM{ؕp?i4SO,~mGK͹xioFxEi^lBntd3R#29E#2?.44/~S\vapoΟ֕ȶݹo[OP>umX^}Alk{I}rGvyMS1Lb*`\-~k0}E~hyYrWyS}b|zd}~Vl}QovZ~uWvvLqcXd]JhkStx\maysWsCPus_{Tn];P\/^B.`P)U_5dP6vu4~Wmϸ`bۦO`gwO1.arנQpv˵p⟙yeggh[p^TWۙ[HcDe9SZ+q/{6Uֳ]wƇpqOnulyirXcok|[Ja[txR،סc}QSO^=j ԻjbŸXXt}q0|RYWndI}DmPnLmWoI_}B^i9OcJmp8k?L@@j8w] GǩdmaAuOg\9~HopQ`Ekp6n3vDqVxV`TwJLfZyZ[^v>wVCv˂Uăig`AslM[lzu>gnĐmiRV̛YnރPʸhҪ}ZeҢ4^{k޵M~X|OpU]fjˋb_ҡ[]Qnsv?XvF^V/cg5^_Bs2tMi\>WMǠf{GNnKsEm;M`Qr2S~8@m*(R:<(fZdy;|VvRՒޜoOjvPbf)v@Jd9Cpc[_A^Y;ekNrKVqvnXkFb}b]ofvUlwP\lV3b GLyVba5̡uQe8td]q¤ؐxtYi=|DKiЙ尢n{eiZCf5l=^@q駾s^ˏfzb?￙~wIlc.ZN+Jٗᔔ^j͏pᤍnazU_I{S頯ۅgPZV>^hOg{KkLsNS{Evht~jwmj\yEk4js>sGs[wfxeoJi?b|@b@EW3I_7Nb.Bi.Bb9U\=qi;a};kM{yRfRT^bpV?pDnntSZ_vwTsL‹cʬ{ݏ`Ҍ|cjbsNt[:Gm}]nPjdJTpuyjQcIh~VyGflҍɋpfh~WpPy=LhRla/r5S5y:dL|Ngyݥќ~anDdHPq-xDsVL7L.T3uƕPoIV`4tNجȗ͌[`|ɧ{evXd+w}BvSgNm8OqMbl`k=Zn@ta:sW8fu?nmMx}DkHu>TexNge>\s=ZwD`y;Vk8l`3n9Ecf]hHKUA7_M*gW.{h7oJ^rcHP_1j_tmY\\j[rZ?yFu\ei{=Hύfuٱ}рʓӡehrehtJ{OOUMvPt;Ẍeńwm|Cex⾉mbdo^=hBc8VlJpCIruTG|aĊqa>npWn}oj}aZuȝ٦]fdLZh3sb0hu1{}LacCn5Qp{¨Ny]PoqƮUolyzqIFRN~g}qYVCKd?kl#u<\PxdJUwj{tTZE@szeOuq|X]kYt;k_-\Mn.ţEʼniϝ|avW]vg:r٠FoBuq-~htz=Mac|u|WlVyV\Jo7UwTSV1Xc-z>d]Ѝ͒Ru94S ^\%Ye3_u2g>aDZ-Y}+}4Qeh=\߀xϮRG{qETck=w{S}vFJhT`v6LpVcu?bn7ds6v8_椵|Js7|o|tauEx9gBVt1wl=V|[ɣ◐}nwtg^TcnmEzPur䮐yziLi/XG-Wj2_-`y;}[ʰlešLU0KY+Wa0oBuӔ_{e:F"2E25;A'S^5`p7UnNuLeqh}mAnm2lrGbK}hSsl`fji`c_ramN\w=]iAnmN_hGVr0?e+Ml2Sl8_k9Ja#@a,Zy8Nw7Lu.5N*Ji<[IQt;]xOtWuRm7Y/TuZyEdCbuQzSfqDgrirVn\cw^zb|km[vuSc`JltbYwwMbA\o0?X1=`PsdWqMWmCSpF^wBUyTv`lLVqSd_=Vi3rb@Nh3MgNdQ:Fd+C\?BU5LW2Ub:Om=8k>BR>:M$8E.\E)AV"EW@O]&Y]+wa8^6ushrOsuAw>CW\VfjWgzIamC_zKOwCMl?|]2w,[UUwiEnDMW=QS*U^6nq5px?pr_`FW:€KӄoqKUaQ8cc*mqDgNc]zx;EpE]ϋʹݲqsX/Dssz^sh~Yhhkf_ͯ璅ym\fWGx:Sb'S`/ge1xGive\ǘ͈pfJXp7ՋowMv|JbXwvNfs9]j?el<~M\cdfZo5{El@nZFfwuonc=XjIwAVZ'[yeʋ‡tLOm%zWE[rʘwkqބqbULzQaluZynƊy|hyX\lrw\XU\{wJe>WhDvaM]ߗmqoݚքSeE+,]W@ËX/HI)h}7wBmtOy>v8tp9qCToԌ璧]qykx}Ń}x~jexXscn{ScSwViȎR{rETjv=ZjGrrXvf{{gtϋ뤯mPvQvOnCqC_vYimzMWp@Nl.Nn+Qi2Xj/Ie(No2U9Iq?mCOs2Un<^z8^?Xh=XiDjG\x6bSlbsexҒraiYvV|WrY~FV7W~Oxd|mMQV1VS@ftRqk{mMdA\=YQx_yLlg?`^xLd|Eo~GWwRkKvkQnQxoj|MXnRjf~gx[vrRprT~GUx?TkDVsO\xGx}O~PnT{\]vNhwPphF9k:4TGKL)Wb*PwC_b;5n<9T@=>!K;ZGET"]T8^v1ak4jn9m9wEyxRzvMlnTb`Gin9_wEmFo4~F{_mYJiLpUac@vo?lOT`amHq_+Nd<8bFLV3Xm4nyAvPsUgSQoMqc`PBJ5BՑmjmbyKwk=}EyR]F]xXdm6a9b8`˟~_fpo4YwPz^Jq~T>ltgghogn][^oUKrCVp4_i1Se/r*RwWW{dpX[e}Trr_DhTWTrJӶOɬٜ̭}ftUwVwgavkok8fG{;T՞Q˅ZjftqsbYnwQyeFώ}vZxRIpo8Ov~^N]ny~8GyT`TcnBss:=zdV^L񊊿kJgKrPZNa\AEm•V^f5Tg0wygHcHIB$}c/`qghUm΅qᨒ^s^aS)^>xp8|@ES\xoPidKF]`~ܜȆ؏o{Doh9҆>|zLXzT:e#.K2LPUG䩷Ւv~c|KWBPGc#|r/sikOZ=ax.x;\Q|KtF~G{ASk?vBzDQueNIjsYVgcVlvanEpp2}oRsy\tzO`n;XnҩȋHuC[t>9@D9#{[HkhCT{QqiHsu;Lb0pF`Bkǟ|<}Q~yו鄣rXdpKsRqfakAm{Bcz@P`P\vCtO~]ulQkT^t@XrR}dlSstt龾᱿荄blp\eD[~Qp[lP]rHVo2Oi)Y9`E]=]JP`9Jmb5BdCOg5LY>YsEez?YyJfTdy@MR6CY?Pk?wTc^uh_fU{UmeAIh:L[Ggj8d|AYS`wNI^<:UGE<,KA!_L$Bi5aaJvc.gEr|G~zCsOs_f`GnSRI@VW+Vh4re4tf-lv>eUNfxXkZsC[g=mmCKV:g[?J)ND!QG)~`49`dkib^fjqMj6JoϕwpRqF^[HtRN^CF_8AO/j<,>Csߝrm偪Дuft]~M[yJeyD9|FC<+vS/E]€dqH=QBMjOV\,u,HomxdCkf5UMOwIlu3rVGЪtؤlp_UXc{JvPt7c|&6EҿWi`p~yxTqm`mbugt}Kq}DQrj\qhX~KVqdk[lBIU^k_jGC}M\NDfa};iyuG`K^=>諶IQ*vwLȣxgAWpi{[RړU݈ekdO:xMZvqEb?\3by:ip5|_OmXOͽQſS`W\ZoՌx}Iih]KDe\7aZ>rAYnpB}wxTpEk[ݓzbMgwސΐ܎]`=_pCtUdPpE^G}Fat[fldlb|prVu[r@s]qOwclfZ~krrvUiW]pUhRPxA\iE{I}^amO`i|7MX0L_';S,U]Aia7Mn4V{X{u}dego~xgvayX_xUpht^ynjWXiCdzFIb>ThD`vKew_vkmuZwRnn+HJ*HS>Xc:]sOrnczWor:uTAobÇ]}GeBTpCXeTjyjya\KhwGqrFjrR_kMRkBQgJKlOYv?JkB]kYpHcUrQbp2aR|KVry}{zlmSwKxEs;ly?It61C4&0#M'5kGbyNKmCHӊ6哙y^g`b_HX=Zl@t0K]]^yn5WH_tGm}84Nf{nahLoETRg}S`o_[}{asyMozhNs9a+IZ%~f$ŒDv˓hzVVlldg@hx?f_^|LbvJfc:v>ZdWqUeP{W~drڝo]Hb[D_y9|y;O\ZMSłeR|~W~{\|\UӐ׎{tcWʴhnzv왧|c|t4`^1gDJ|`nUi=|@]x]q_tSv)Y\#pq7B^]RyLN_ii[y(^$&@=@7MWQx*3?J*3'K3;oJ6~rJBTBJae._.9'Td e}GozRjSbK?{qPq0x1T{UG[WNt6F{FQTUHmjkd|Q䄸ngϹsRUMnʁrԮ븮eQdt7^l=e`:Y^2`i6jyWkȽyfLZa˒匃bzɅyWi=X}7Os*Q/KR*zSz]wu\~ERs6\g3e5_-k}.b?/k5@śpƭhνhяaȜ]^;dJ֖ecŃ}Kv[gWxctn厔~㜑ʄ˟䞖gT|HT_Mp~a~^lvXXv;ow[ug{wXcBn@`Gp\ff~ʋg{nwRe1k{7es3Pc1Y_?Ro9Iv@bdׁ󂃺`VaA`ciUjvBWlH[h?hz^Pjd8fs9Ma(CP6rnHTg0[yHzg}jgQnŢǃf}vse[pRyXgQfbtikNfrC^e6R^=_NgqHS^E`pKYxJf|Qj:Lh/K`5Sy9\>\SwexP_cCgdD\ukaKe~KTvFYMo]dflvzbd}PW{Fj{LjwCRk4>T2:X,hH(~T5jBHc]s=mf:FGb?"oV#g{EhpHdcH^dFYbJghCvkHwcFilFglIrs8f=tHMytncG_VdFEy1FaD`]3ca'ku7dz<~lGnsMYhW[_?U`Ii[Fkm1zCDwhxkHoBPSbR,\x9cb=kFat?zJCG2e-J]yN{HgYgGHnRQPsz3z=fg[YUk]eJzn0{xqžwTsmdU|slVKn͎b\V}Ga^8x'Zx~rj]j}HKWVv`gUcCyv@[RiRTjmnaUTXwbc}Ve]nQtw?WUMn;On6]~3rA}^kOvDTd1Yk)6siH``ˇx֗uzrw~lGgtHxބkkZIJYʐtVN٨aX}|2F^]b@c>BS`bf{sy_̽`}Ix16h Hd CV1_3BBe!/7#Q765~Onb5D=>) MJZ^5P[,Qjbu/=l-Ob,|5rNQYKULj͗xzj1xvDd>lw4s|;TY[P}PM]`ZmewÕclnu\ZˀxÎ˔nT>bG*|cqiLUh+rt>~Oҩpϱ眐p|TqL|hJf}J7Y)wiPvK@l))>7DSk,hn|yY^vR`w9e5ay@d}1[b9[k䪽pf½vKr@z_ТΚ|jmHi@~ۦ歫袙ǀ}tu^}H]@pz_g{Mf>Mb5ZX2^t:cT6PY5_]CfvCdufphg^mErQ_ojw|tyady3Rk$K`#E_-VHhbtgUc}uct5V^/^a9tnw|PfsC\wDmP~:]d7aa/OS>}ts||mwjqeksMqgipvWUuRsURzUo{es=MR6[Z2MZGiuPVh:JN/@N.BI1B[>gf7^p1QsM{jjoGRe8Ld<_l@ux_sOYpH}Had;NlF]z[yvvnpwQe|;_|UdvWe?Cc*5V6=Y=HoVl^g]qvxxfIXs5Ka6QlNBF6WW)_j4YE]kT]B`wA\~m`y]LvMRVCcP2y[5zvEZ`s]?`c3B@H8)l=!Uv3VbIUf?~KjbY|Js39o90A/,",)%v&̃fcDlzzy{rfY{=UqqVpGTQqRUxW{DIT[`b}SZ{5Gnsb|J\d_tVi/ieuga|sv=Ye{Y[ooxI~^uQUh?yW,6pd_v{jip{EnZvF|^[aboBlTP~CKp=MQ7hU)Q9KG6CX'dg.PlSSOh>Jm3Uq2^h4fy?hKhBzFv]YX8j,WGvw0fUhE_G]tS@`l:Ƌw[{bvHukDgn֐Lp>j)x祈ӌg`RMܟ̑f_s_8z\q|[вP\CsSAE]nXwMUz8Cs-{3xqŠ^ME|0DX,K(,T7&BI_r[nLdxMKBGSY(e_N`&Pt$uw2cx}ihm|ksJANmuPw}9y6q2AWZFIwV`ēcQZU`~TyDR܉duˁwznvSճߎxUN.S`x{UkC{K{zPi]A^tyUXlY[qr=Zz8Hv,YY7=J,4;GF.kF_s7\jEXg,xBwu=RxIxpAw}9p=ߑkucUƔgtù^᛾qٺ`Vfϟ}cNn>jB۝rhDrxKgdmyIrGlJiNpJ_p9]n>_DT};krBZd1}u8^b5Db&Sy,Eq-Sb3ceR2|yxwo|Peg~fP\yX|xv_pAsd5tuRdl9Ka.QbC\ua`b.EG,YeRyv~byewgnujpTlV}Pdq9a}SogiAj[TWy>*5U&ulMkvFROUdW:qC\G~~EIkrUAk|mK|Ix[Z|L7anrpJ9tNwTb[s>dSTjN|iC[tw?ōE뗂m[H~A``p7kSBmUyzIP|OMq~fIoKeMcMhSk>eP\{DC?Ed+iu)g1ZYt=tXgschhMC{G9=2ZT%?m7G_(c|'qyDOgfAcmM9ey4n7YAZ9`w;ZEs8WuxZcjpjuBhK[]ώDԒV՗ÅfloHpl> иkq6EAlb$|Sp>OyMHY_eoz݉킊RDl9LsۘӭkzIF+U"C;)]$M>;CTxVt!Z'BY-pu,cǒζz[kv_u_\rRp٘䒯}ϓfc^Dct1~/EH:NZ\MҘaTMYBdjSZejॣt󾲥owIo֕]hfeuS_DvDOzR~fEPoOSU5zth_oMFV/pOGܶgFh}tsvRWk;Vq}abEyVmTNRU_gbp]^Tġq溃󹂩a|^bѦQsPxcregAjsdZBjPxzuStMef@n_[{NsS{ϋtpqPnk}ruvoViVqM`H^pAkFo{CqZ_|GV\AmuFBNs8C]8bZgeoQa28:2RP3Qv1>Z7QYIXcAymKfU|nBrm9^i@sqa~Tw|HsrNl[uYqRhPUQaz`iwM]:HpAT}KkenkRöksRiJTD`sHsxCgV,ZP-L]5X\2HJ1SV8VaA\nKd|DYz@VhARa5SYC{{XhtJg{F_@YuMapNlzOmi;PZJyO}e.NeAp~Wawlqt|y~JXuAQpGW~;QkDRn[ucT]4Af9HhL^l̫硺ߘመHUkD\wKUzBLMF@J7985GE,dP/V].@M8L5CMS+Ie88_5LF/^Y*bd9ssIcfNmnR^O``iq[^dClcBao3Z~AOqGXSDOV6hL8c_7lF|DsYzkc\|X`Y[oZ<`q7Qh9VJ/GI.ZI*dM(^g>li@zg=h?zgRw=JxEksPj_@wsAfRc~[uLeH[^gBC҄{pqx8N@(P-!u,эgvjLHVbi@ksDX`[y@LJL$_\+lq@pLXQmJJLjycvFrWxQOr@z?vo{LwD_hLlNq~EmQUphxpQ}siSl|fmY[;}TO|bEcrbQOkn26{V_mrnSjbr5ReHff8iJrDRrewcNvhXr[x4yZXVkhfB9l[\\UHg5V<^x9t剌zQjwր`dNe}C|LxM~\AV_wraKZiər̄m쀢ll‡yt|wc܂ʼn鰐GuMgꈫ̇O|^t^_Rp\zWhJn[xpzdeڌyU`;fy@nHeu\o|ۅ|r̅_UbcTgf{kўܐ?=H#GH,Qg)Pk=nQmVe^c`0^a?fh=phKm]}lzz7O[>rX{ZwWpBI[1HS9OfGcCeOoĕ󲟾Zwo~lz\|MgLi}Gnr4Ye>gvAwo<[^2V\0FW=MdZo~RbpLlnHij9NaKuSUz^v\v\mQ^rDkZCaLj?h`2Q]?_qTnrlctVgsGShBUnM[m8EX;PnQebwhM]CUqL^ucuyъΙScK_NmOIPT5:4,/.=,'DJ6C*>9)S9#KM)PM0Kj6[e4`k8Sv?TgOVaCv`Bv9RgffeydfhCiHxUnyVwRBa;m=Q~[}X~zM~pTurEZjORV>GT4NI+W='`H6XJ,fU7d]5~`Aqd?_XjEL]JlVRZDq\,bMp|LzDNxjbe|.Vg%&7)&B)g.΃̍|ZBoGfaJlfCUoUhM@fN2Zn8nm=NosXe^vce|XRqAciHmy;`~6dx;t6tVuEbvApl;Do_QZĊs~j{lj]pWi7z5{X`XYjsjiHR}Fck7\Rt]toa}S^aPZHrvErk;rm@hg7hd1_g7MU0NJ3hbDo}HXsHo_z_ouzb`qAcbKxtJHkl4ViGoxMntXpOTj6GM4JZ:AT4AU8AY3E^EhRmRe]tPfMiM_~R|kioІgyVqYkNFQOE6:61-/A<'B>'XJ&OY/P]7Ll9Y_wg1V9gG~]Cy^=w`MlfD|bC~=fZxTH֥k}f`?}-C>03%"& 9&n*p͟]pӖAs\B@-A=$AP$I`*U.?deoHjKgJ9o4sYqsY`lFdxL|:q=d?t2^[OLXpZ~IUZkrnalwxhC{~Fr@nHs8~[zgUl~Pp|gy9fʊbJime2}Kcjχig^CUfVzKfmNVJV/rX^FIfkM3]e2Vi2q0I}yvfRR^s=qx@L`DGv-Hj(2S,TT"ey.RvE[^2hhGVJcm4Fpp]_\k8aw7Yt<8|N~p>quCE|kOq`DJ}Rf{V^\zq?xALILcGfP6nl0`|`IxdTZNl^:zzBpOeVbkRnlOjO]n`hj\eb:ok8{d=iEtGhJLkyyWWj^hZSh[;r`9Sr9wG>qa-~ZFxb>raHqCJYNahfv8r$0^ !7?!23(#:#K<QD$sK$wLmaU_}[ftcqŎJ\^g\3IWMPAVњrĿ~~GiPsZKsg::l^c=zH]fuhwCEZzn?fYClQKh/tr0eVTtb_^bg;hzPXP+dWv__DNʯzry[Q{_ed}rlX{JywfZLo&g~KB\#Ve*c1Bw^їrds[}r^XIo=nN4_尉x߱fxӡeBMG|FMOVǾ|pƆՅ}x}adlymxXUlo䆩x}ge>vQ^Ҍ甽ݲ闣yuds`ZoVxPYx_m^ńړyqzly͚Ԓˇ]Bc%Li,N7P=yGS:Qq[vfsgE7X;vkҁ˭ѓ}kglgcRTWqܵqw҉ZluPfvi~_]k5Ug3Xi4[e6mn?~j:khLsVa}WqXhJu[qZT`MsMvWp{^bWpYmKazCZd=^o@`g:Pf8axc~dij^vDyZfwighvi{_tXqSZ9Cj0TlKciZDUiOiq[i|Ma?W}8KxXt}yj[R}uLsR}MxBkk4Td8Vb5B[CLyoyobl@Hb@JaBSk4Ib3M_6OlhUqOjtQZtHVu=Jb8?Y+]p@cl?X]7sRCri:`i>fkCgtEtvNk|PNvOK\VZJ?Wg+\[2Bg?T^M^k;[e;\d;lz@JmT|tTyVu`nnXulDCX|K}NuKHlt~trpQrEAuKfAn>mLmMiP}K\ϤotuXBzd#HD#&B 6.,4*<-j?6f,6J3LMf$Sm~d\?qˬ̂uǦnvmQclo~ND\n͈VOv{}eRikRGxWMpehb4o7[p^x?qWYrT~LZoAZe?KTi߻_s~^nmc?\VXF\4J_,Me0R^*Yb0Qy>tv%PDžybMzuRD{:jwmKhXlK^B5tfv_GpOas1[~;;\ZET8yl;r\`kjqw_S;pwVVaUZ;jq6Y|rflxzebxɋ[aj,A(O"W]bGuNk@KwTS|~jUcZSJ̼襁|Yq޷羱ߤޒrkжryj{D?8HY%Zy/DWΩqLaz^UEjkY[~gwiqbi]cQfMkcrerVm\ev`oqudOX}nwrWev9M[5Rg1Rz+J{/Is:S~UlmvXpQsGrHpKgMdqEgl>^pIbKeWc_f[^t[dxDK`-BU/EX&AY(@T0H^PfYrGbj=[d4Oq?^Mb@hcB`t;dAtIEXC[Ts@?mU2Me=@O=<=49B:@E9:@,KF)u[.tHfJblEsDIYiqsbi`Ltib^i;nndhZDEp:LY*YN2b^FuKбqԒ\^lfmQvKYq]f>tq7wtL^cppeE_yJYL\`0UKJm7O>fr2Fx`h[b|7`AqpJUdzj}O^Ol?T~MQI6VvAHnTa|r]ICz>TN&qh5JyQJVk.:mf̈́wvIuVJm[oHG䁁TpztqqXwVLƋ㵺㾎یol\IE6DW(Y]&BtfW{І}Xs?dwm讥ŏn]\{{ˉ̸͉߱mrha`JgTFl9Zi'mE݀7G0x4({XxrJs߹~ȏ몹ۋoTa|s;\aE}Cipp۫q϶sبlxVNwvL`q{Scu|Lf8[5Tr.\w1Um0ptvl=˙Obl?`h|udsbIUvEƍ؟쬓^R/RcE}SjhjSfLkfyPO{38R$/MZ=Pq+Kn,Kr8uaЎ曧͑q옪؛ܨ֘|yvyxmsZuWiz[wʅr{{z`VJpzXsynWqIe|wDIi[bZxaI|B^yb\zRZ\XW8d`F\h;WqGsY8d8yJRarqn]RȠVuo_nY)Rj9(0M<"`G.{Lzm+!-L &(&>0Q=k=^Z]h^bjLEaKZ:GaE,cuKm9ݑOrїStZlg_M}{EiRc[yO\rWgPuO|Qod{UWhHRk8X[0UR1RX7el6qCEjNYIhAHb-SR*]|g`EzM}SȾ~{ssgPSk;qBh=xxAdOx|Hu蒲߆`bwEuw8gw6bh8v}EYyIqsUNo=b7]|6`2L^WB3}^nx5P޷ڎ瑔TS\p>gnB`{[nŒߙܧee@Vw/wyG`k⣫זڣi~YOiVIv($>%9:J+Ru;>k*-I'_Pf{r_^̑vҁfN[yHuLHm4avJl@`E`@U/VqGkk;`>dlL~}ܡ磺搎bfER?RwAJfD[{h~aqH_}ћ퟿DžɈzxwшnzՙݕՋ}bxYpL`Zrf|hrĈҐ̀ZnVmKpXn|xƈz|xcyVc_mWyJFop5LuA\bu^oGT?OpMk^xhsaXrMe~OeK_x?atNdvXd^kQ`{>Tf9Sh>YlDTp:Lb1Na6Pb2AYHWybhlo^[n3I^3Eh26Y9eZ"yw?M`o{{bc_h[j\AtWJXnMRxWXXSho5j@c]_tXxp>CUggnq~WjLSoφko}nVgESC<@?)408,%&6 h* e'Rc(Pj.,+04/=!-6 d3x`(p|aiqt`oc3XC"27)c"gn3k1Lb\VlboIMi[Nf`wq{DeUeHp?Ibv_XiSLj8Cb-RL&>T/BF'b^.];NdMSe3Tq:Vh=Cq7k\*Bomw{{^|[}n{bSss{neS\Z{Ddq8gpw|H\yMk5C{w|^Wj刢{̽×j_̌inFy~L}y=V龔iIq9ln3vufg8CE'OW,uYǚƄm}ltQpAxKlxJǶߑdoulG9i)XCiSpdSv9\nYO<)NX)_X5l4vAl[dwkZmOSaP[S7[S4l_9mDmP?t_iabvHZmwfpmpNrLj\ut[~SQÀazNB@B04r;%Oi3AB61L@50!,;-4 01<2!!00/'=(N9YX,5\8;B0CC4O-U9(B;+=C-?M/'I'k!eju5]xt{O};guaw?r9UjkpzanqmcTQu>KkBwY/{Px^qNMyWHy>6R,UL*Re4Xa<8X|U[i;]o9[e6Tt6Dj5oj*NvxaqʎgshIWkJ{?yzWta~nAyUmyPRwLjo8Rhrj~Ktkt|[~SRg|U˭idcUKfyLNmCOh3tAyQgOFiŶM߃wu]Bk}_ym[ɵrubğS|twRqWtBcPTk>dX$e?lwTsSlRnMef|{wUa:Gs8Lrgxzjaokz^kM`YfYYRNo?D].A`,AY4Kc\ij~V_{HXpMfkMxfEa[=CUP\wThzAXb5KS8^[@edKRkGZpFfd7w}H_cemaw^eXDZNBKR03Q.N=.]L Pj4X`7J^0MU0dR4fk5xm`~Hu|BXiĢVqvwGizLr@EJ/^p5}Dp|^oygwgOi=b}o^bXttdBSwAg;Zz@Qp4CX&Hf*iI)vhIzZjGrAbUurBweFvv3vqmuMrCU=tx:j{:Nqd~gK\9Nq5Yl4p4e;z>qsY]}pTbDr)X]kqUlxEdhNǑKE?c'YO&g?S\upUڠ~kVMmՅ•j~L`jQ|;iDsMmЇƅ[:Sw S{-Te'Xʈ³矤勋~wQzp|5cۂޝ׀|{GrEQqیpqVStIwVƈǎгȆ{V`a.Mb2Qq4rKvT[h9mS^j<=Lcb+yC~\ɇwjFR}1Jd2pNfH˓僟}nᢠ|წS^yCxkSxZ\r{ɉfVyym~~lX{V_??l,=Y?q{Na4W/R6[yAuctx_DžЅdiLrLYK[iۥ{yTqXn]ikka?>p0*S!4R.RwRnWbOiq~pv\bbnczh_sXz^od}MpzMq]sMag[ϛΝa[dv]mhƈ؎yuaYmzxy|riNk8La/Cc>Mp\kb|byZdOQahiebqioru]^t]\q{jiZ^wMWxC]wCTt>Moaqdt\rGbeBhX>h[=b[:WW>[iXuNpsFdiJeoOsyOvR2h9HYk|YvdxigfFarJTeB@e;CH9QJ%H]'YQ8]c,ee.en@fuFcrDhxMp_Ew`DDjGHMSNM>X_-\X5lX5XZ1]h^\Lh9MT>hN;rCPn_vzOjnP^Rf:Je)CZ0]X*WF_l@Q]@hf6WXdp>|>{djaeFBcA`X'mqFxP}wJJW^uDzu1]~EMbAfp5Q|QXK`BQ~CLFYg,tF2eImMYww9yHob~p:T҂okzP`oGX?UpDNq]whds2r=ta}fZi2hu0ylyII֒7;L=K<|9h׍hwHʬ|MY~ado5pD]bޏ{\igwJTo+Dd#WT+kpSysXcfEj{BIm"Xu)Yc㌵րxgxtESWya[žmĪzZ}hjOrY»ȋfxoJbC_>~T^y\hRF@(oS[v0jxH~yciZu[UzkMxuL~OjlJcxBxNmtʝilyF^Cmf=eZ8{^SXy-Hr-Lj6儰ˑ╉wj{~luuVlW_@V9oAgFpZn}^hItcuna͌憝ۗԋ}ljqjԜltLeL`DhAS=a_ʂrybxPbz?Ca>ajeuckRxUrCPnFjZ]kavK]w@VrNrezdiGbQmlNJֈ׈ʇʋsmMUyKgSbEdbr]h|QopBYr4Gk7LuBSzSoa|[|keg|gojwvlneiO^RZ{_^fc]ewWaShZyNXtNjXn`|Vsn;`V=_^E[hRatW]Kf~I]f>liLwtXUg5;gmqyjloy`vNsKuVfXQ|LTc?ma3Hu3HZ;cZ4cd3^j=^pPTdGDk=MR4dU$Rn4\M?iL0hh:cwOnYuPYx{ݫpd_LÇ䨟;e%/p"!+B }GgFv_KMk;`]fF*Aj3{\;W>MjmDL,i?+\3K^LkbJa1\e;npFvtOdOJGsR>f$pHvPF~^|rQrcfgQqN`Rfb>_lHq>n]|\{VMswHoZQZzF5X4HJ*`U0R?RbEe])FrnlrV]Yq?lt2MyTmXQ@b5BN$]$b`~jC\_o]8;saTa>zApTvW_aZ{=j/pbOmUTV,e8xfmRr>l1yrE`~O[f2ӉAĿdkV_6r^1_?Dt֣~{La֑solb_Hco:N|YcJny;kF}e®ey󙢽lrir]pBuvCsuoN{cP{Uե}wfTksdhkHTҩލ݈nj{֒\am5[S0}=b3]q;uO[v:SZ1kVAp}Ko~Cbw;~fBy{B^v-Gc6sqS|QQQoXtNU1Bm2R]_γ撫ᕪsb{TZ}ϟupopmLdNjPtbygwnᏢz_tJgEgTyirQcHkRpZunӊgpHMp;>p5GdFwuU|r|mudrLd|9Y{3[B`D`HmRfYk`cWojvpldtixw~nhv[k}vscfHNLr]mhymW_t;Pv;Su>JjF^tSnyUjIJR;9@;OjUYevq{bkT`[`fqhm`f]qgmdvIU\:C\?Ma?OcFff>fnBivYll{gU`yCOX6TJ<]\RwNc?H^k|xk~tusV|O[fzYerTaiHjk;[o%m';bixcR^<{Slr˖uvbbg>d%,U,OxPw|K`^xQ{y=[hmlRwqEH[p5s?:-UTrq2Y8w{ifrTXhDlcPjEPSsPUq8G[sML|{7ѣUyqdrWSL[V?~e4RRbUMBjj7qzHSW~oOW@NH*J]:j?]VgnPEYhYvoUeMPjT^i=p8\_mQ{Ewh/OzkM@1r_uZjRZF@m=HR!|_3qRQC|n^5DɢmׂSLVi7^l>ny5sQWw緋zgmX}xG[sDdY0V}Alb/hH?kchDx[9t{UeQN|]Gb\U|u7HdgMnW_uKq7ty~D@S9a9fGnP]~IETIt5spI\a?~MaKfk7|2̕\rvdcpS|Api*bLxIh@z}DwMzIHqŁR鱆lqUP؟ŎΚxQw[bߦ׶Ƥvt\xUiߌeuSlM\c2XYC`lwPYog큭UCW^GqL{WsU[JmSAaWvqZ{h?Wc;Ro@Wd=S};CO)ZV2a^1Wi,lȗsVJVk񐓘Ze`UYla:qzC~SxΓwۄϗ鞣wygɋܞxHS3kGHk^Fmj:sNbuvQa`hÚͮ}gbfYMxL`]^ZvJpPn͎ӃwfЍ핉}uʎ_uSvYchFrur[X~FgqѪ{oƃߗєp~vmu[wB\8O~>ebqsWme{fz]bZl^|_s^{wheYQwCktWvZsdu[u΁moECj/ZqKjb~owj{]nXgSyd|wreoUX}KIh7FS3H^Hi`zk{musvۚzil~BXr@Tn6Fb9G_?Yf=Za<:Q"):)McKh{ZbyobMHuV[Vcb|YjP`d}p`x;Db(-P&*Q'/M9YrOaSXlqckF_|9LY;WTOppQpeFqCyUse]jyps|Z}NXt]nmSplNhb;om@ef8YbDbc;f;En[9iBHDhI#r0DjӨx㦈̱̍ts4U}!.H"! HMPQX:P];M_Hb8w7xc\MZCypmU2p.wzDvTyK_W~rh1BXabsT\ęҏ[kDzWɆ]yR^yv@iRXYin?gE^V{cZPHe:GH0S3Wko}vmpTo[dTLcIVi4\B[=nzHGTQk/ZQ*_j6dfA՟6cשhLKkrRyRK^5lg4o6k{ݖk|jKߕ繧l{KweIrTuqItfqiz5~^rqGYvDaa/_i8s6`{N_v;jr9`FaY4GTL[1stoKISy|R^p~Q[cIsxIIrӞ_@kn7{SrjgzGFzDBM)gA*YSyL<'oV|vJHvEqu;RwJCyvFW}gOHv?xǰ׼ګiЉiΐ𿤦fz}ߨɛ৽s_Ƽ{p[ǺmdSnӼ}ƄPi~\_HL\PdxE_~BQW0ek[[]<~bJsjCxbGpJ=eH4Wd:Ww9>S-@E'BA)ZJ,jT:gsHU俽|ժڜʀ`meN{L{܄nM}POЌ҂ߓbXji2cU3\[=SS-xIUjx?`wGdxB~OwS\jF\STw;j0[i1Z[|ZrÃ|rϏpTaJyojXhqDmIziԋufI[yJc{AXs;RlAtU[Pkx~nyѡ_dh[qlxtUhcMTb7KaDvWiNz[fO[P_PpQnp~~Up~Hb;XNqVzrocNUY9RW]|r|M_[m]l~^rGH]+9_GejˈoXi|OirRb~N[{Yg{QjVbFU~8Bo5Fc@ThUb|Zkx[pfuӔl}Y`r5DZ1>L(:I/Kb;Sa2KZ5K_,;N-IG$@n7T=+vb2EawO-ʷ\OK(~m@ZorHq?k|@X4taMXSZcs6vFzFdnvYabV}uEźnՉekqqE`k@yHUl|EJ\yXDb0^D%mj7_onIpEcN[@>}_~7f`Lx=}^qVWl.b|5yBW?k{xoWѫrlf|=Ua6KArʪx߲mpgNV쓯߰ӎԋ]osILf}Zq૪}|@l^FdmnJWg1h[5O}Mլrx^{WĮh_yySetGRN5u<=oC>a`GL?)_k9Of2̕ʋroumaq;U栤izVxT^xܧ晫‰ͳhvطcñᢡŘlhֈ}}XQ|FlDj>sNxKWt6kqBsEWntvҢvo@d1]y3Hf+{Ti5_[BqN]Qoaωu~ˋĢuiDgRږieKOGRDXt>FT-O\:}g}]aaqVkwVtcRn0cT\=OqHxR^`Mp_{c|q]mR~gurMRMaRUxAPik9:n:3X:@WKry†Łσ}ME_$3C$8N2TiCcr6K_6TQ7[e9UAGkCTfEZdWeoZer^uȆi~YlQlMIp:CR;NYVkwǓt}=D_Xt׋Ém{sS^o\u^m|h~IOvqsxl\daw^uhBl]U`oCi=sG{NlOhpM^qQe9Vp=`i8CwHBM*\+ß`ښżrpJ{DK\2Uk1Vp5Ij4xW1uCz}lF_Ρto_ySSdhx9ai9\v;oMX\PuL]~TOҒjrr4KbmHR@n2Q6+Bi,>>"Yy9jz?VE`'bZ2wDtvaRHu7tvJ[]=_r0Cv7Zm*[W0{̚ںꌪqq}ОrhcQzlJȮv楂ȃK|Qt|Kv~jY}SfAa=rUqgI|Id3uJpJMk3^}AeMpZhHq^m^dOϐyyyojWVq@cyNyz\kfymvkvkrMLh1Yqh06[-=X8FYZ~{qwÆ啶jh:K/L6TH^zDZd;SbJnwđ~hyXkOK{6HmKkpxcxWV=<_;Z}\yàٟwj:D^.Oe=VpK`uAQg9TdBeWyau^cZjPUr9I^7B`UrtlomwPQr1F]4JjLSl{DKnUpusmfo{@MN,CM=Xi`z}}vSmX_|qgug}SVT^t[x\BqfCr^L\iDZ[FoZ7_>~JXq}`fwV^lMmYMgADYytp}iʀXRxƜšʧϧȄxk`~YPxYu5MHS%,"!)!M1,O 50)+<"-)"24/729 _: x8rW~_gJV|wGtLNS*`N(xH.i6Sdaw`ͳqYgMjMSKa\5}.Oi]xnsUeuWm_@lm?s~KI]vx{dZe@tKumW]~UG^CL[-2d^{tBuGVLQpAQi6W5\MMi3Ig5}o+BfPSh+rq5NeFWsEjx:xJgZQyKPe2br/JtXT0xbF{Pfc{m8p}iNglq\NXFfnvKjuBCƐaZlAIT}cYP^w?wsA`{ZSOMnꒄf[TwQi}̅~Ĭv~inӋ֑U`e^;g@h]rgf\bxNWg@{lyZKw4Bb;MVC[fB\qFlt^tKJ^2dCi~>chH[lQvҫsdsǃզߩxk`qlojpVfzUhLuzKS{x@l[yuڏkolRpJhnHweskiiĂnp͎ąd`~csfxY]IbQjdhqYl|[q`x^tniNQoLWqZnl{dYakR]wLdg~˨ܖdqKkDmb[qKtPlRtZ}uQ\DLzCJhGYgGb~Zh{d`UcQkVlcyjsZ{chnllc{dtSgz`?7dYJ7ii5DK#qZ)99$($"2&i19R)KF66b+%D155&H!*+E:O7hnjwNdkOwVZX?bS)Q/o<~LV͹Ț{xqZEuM;DXI3626PR)\Q/ig8~[NTtzuayfuHYP[rtiSk[|b.foK~bJ]QmfMmT4[qKsw0OvmwVI^jwW<^FGM$OP"Jq5cj,tMHXzYnvCDoFK[)ba)enA\IQo9\6b|Bm{FlRqUWj|z]I{VXt9OI>^6O*ISn/kvuEMVKe7Xi+my1]WkQAz@MbwoyGssLqJwXEi?He$wg,d樅Ҟu־١ׁ}goSiH}QvKxjTg546Fj~znTd]bx=QrVqgVm6yu6|hFmCqu=s7|oVnNod6]K/h>f^at^Q;jviynЭ~ӪtilRr_r󗂾t`;R~\lljFrց`exj]mxv@cq7cb,mVtZk>wBT⹕тbhcŽ}|psdfeHi{WbO|R{`aoRh:nBHr^MnqAfXYZ(YA,a?0B5%J>'kt@ʋkܚ㈠gjg܉߇ϒyr||XwლgposA\0[0gBzXĜvr=O{(Ht)qXwvrLRk?M]?8O)HS'B]1d[cTyabkYcPnqyMbJ\y9Lw2Ha/Ok3Uf>\m>ahC^NsvVxW|Uk֔~e[ԝĊky~L^w?Qr;Vq>P{Dh[h`ulq[[AeAXQzuяߤqpSIz27Q*EUNٙ̊œјxxTrVwUvUthmŸroO[['@V7LzIXcncUr59\6F^G]za}kwQMZ4HgNY˦ʊk{Oy`}krVq{RsWc_z~xgmMcUfLcPfRjZrwx]rD;H<>ICP`W_ktmqZs~QjR`?Yi>izWjmobU]xjUeTUpBWv;\tEfbI]8lhNbrLoKpCwwJgp\ZfNRn?Uo;^c>UzBd_@gX7{]=_>gG}sKnNxRQjΦ~n}~}֤ݭû蓉kpelMLaK^JPY>yc@Cbmqv?x0RN&/! 0#.B*P2AS3^Z4Ow9SmBKb1%]41.%O:WD{venssUaJVkWi`N[lAmf9Ch{yUj\gYscY7I8UX3k`56=A{D,ClzVUeA[1CXdRH8ic:\?ږtksFaQXzH`rHxr7oTfQQzUMK2rW1bz@YqJ`[9ii4azO~=RzfDs?H]1s^&ZIzB_cKtOqf3HAqPYP-xf+cNPpE^;Cj=Vj)v8\XG_Ffk4cGi}|^Vy[sDx=0pfQGak;~Nckk_oȅNPIp4Il(v+OwVc,iv4mH~zBǬTuxqJiRhuMABg*Na"l.~hqyЫx~dÈlfMC^V2^Q{`wdqrc[br:Lo4p:ײ~鈟mKS͡ePaب|VvQ^WZvHwIasdj]M׮}ƛņU򕕝}Z⭋ôpofv8lf?nsJd{;x@\٠QA9<ZO*uMyMeQoR3V^-TK+mW8qz;~Mu㜌jmjğɀo`{Eq]DvωƆUm}Ie[ef;Ue1`9gN]rkj@n@Ly/bCYXd_;9I+CN)MP.Se=\GtR^Rgr[i_{hb_mE`r7CT)Ec4XJ`LQ|JeUvOUv;g|ZuUxgzXv|V|nw}ّ~uz_oNQOaP`W誝ә΄]oUs؊鳭oo[^]UR~7D\_Ϣᬪňw[eR]O^Qri雿Ѕq`f99U0`Ob\6kkMyF[nKTn@^j4z;Uq`LxDGb/En)gX'jRn~UR[\o:yUDfMO5Ud-im7TH\s@XrDb|C]DrAVķs|ÚfXCE?e%cnbov=pQ`o=S~vϜw_LPfCh,Xh&O{8~=ZTc.Q['vg.~Lq_acc[p=PeVm^tbnZzdYc~KZjBTd19Y0IeCarRrdV`w;MeF^u}ou^u~Nfq?\n>RU]O`|BXqHoSgGX~E]xJcM_y_^v`o\zKfvMrMi\|eLlKdLdUl~`fwHMpTVTJ]U5Nt6GrKZR5rj.fGj`\e]rU~ke@Dxxh}c`rX~{P|zNptUzePyDdc[npitSX,JV!jx-x|=YbqGYw7j2n|AUڦbsVe;l\ЄȪ]^}eXOHư{蟭ӞafkWqwT~Hi̾ҒWSj5nm7sSSE~HOraIR|LwSW]Zt~?lWvLnxJju}rM~>kL{kx}Jcᜩ܈އxoTodoԉ☳wHWƩiqfKԁσs^䌇nx񶐷uā]nTX{fcbqennْrÊi|RuQhy>Yt|c[l値˂|uɄzrla}ZpZlGiMƏԁqaHTyBYmGeu?Rk:fXwsmrvYf|QfoYi`lbu\u\|_o|`sVuDnDXqHxqm{ۄxQuhϋyan[{l˞\rGaAaL\I_E]{[{vpXUPStot~Ρ㘦䚸爳LJsyƄi]GWtXrmsfwTxJ]vF[whve~Kb};Jg>N|IWQ]Yb[cHaHU{Qqu7Q`/HlDOmur}_pa\l[`[cM_vCOrLihm{\nUuRgVb{O_x?Ra8PkGZz\<V-Y~?b`Lad._XApc7}CRa]nFpZEV7nK-ao7rMXϚJрјa{دnxmehmLsuGFd^R>km9wnPqdBi[j@LkPTlaf^8VEs?tM_KggGDwDB{w~fviN{SneLL\cgw?se9owKs}IcJe~UvC~wKn{MO_>Hj8_j.n@yRyi]uijbSx||SImS/V2+A>AVb%kk9bUSVUsKWr>Rh?Oc=;Ts^i^M{xe{PwR[YzԊlLJz%Qe<|eSqܠ㷡۞Ɏ٬yhGxDcr,WўWvQ,U56CGN~x9eXLtdyTYʯmUnRm72P.V_*pDcqMH\,]n,V7gw-ssOLdoZtCdLZlJcu2tw@jnܨkڀ^dhjtoTnUΓvBk2O?ꂝlyQm_DLR"`Z4h]DsFioI{Nmt藺nQSLMvƙklsFKSfr`g]k}Tc̈`p`_tJyiB􃈃Iihaeз|jopiZr_wR˂邔q`Fr/__2wJ՛r؈}o}ԉxNa>c|_tӇڪȘr[ҎuΓׅcvGUz6V\1tnPłً{Ӓt\iؙq€Հl`vMbHgvyUu׃hgsXp~Mk\coZttlsWz{T|[tShkL{^uOu\ĩ~~_tVycqh{TkVlpTaFndʃ߄埱vfvqomgx`עmwJlJuIhR~Uxk]YvYjOgiܮ䛪͎Ԉ̉ʈĚ֤Ԗy|͏vxcsn}skV_zAZtZlYaEcvΚ_Vx3IjIbbih}yfv@IP"6P3QmVo{|~eolttgcnSlM\}JUyVdebYetNgoMZlMTjCK`3=^=Q|WoUh}BRoEZgIQm]t^WYqr;YoL\gN[_EDrFIaBFV4@^/OY6[`-RsFUeM;jAKK2hQ'nyA]~ajzi}XxTfvJ}hFcQfM0q["Z;}E|V~_w{fk]gDMhr†o~ai}ˡȵô߾ɦv|Uzf_L>om?eTHsLGG4EJ+XO-V5>xZWuTiNbm^q`AڒGuкⰀqZt@҉]ceOsXRmfoc1hbftNPk`vY6tOZbbjKR[esAt=Wu;sqGKzVw|HvhG}HdqshjVzxIa\qfPV_GYgIpq.yEvlOYc^_\CEn^8Tf:T\3Pc7Wt(a.kf}hlmPlCÅ֞tZfTEd:FxIWc2Hb9{^-mlQ|^Lh6cb1nd3AWkh\Pr[|r:_fXHdZɟI_7ji'\L^;l?r>FQlbSu\ckVlZ`Ih11T*>Q#g2cNr1yPT\go~RPJU,Y{Nf7Jxq]5QA^X,mLzpF4wY^k:hg4[Wϰ̝إpVR]a{Lv8ax{Mynn[{ağw]قoմڤc}Oʸ̋q݂uhԛx_kW\lq܃҅񅅼faivNyS^|xxZSuv֊vf|ĭ|yVaa8SoRXzGVėޛzcomGv@kbv{aueΌjMs`MlrWlajDkFenBZ\ܟԋgQNfMo]u}]{iwgpsΏlwVtŒ{y^j|BZrC}YcĂW{X}^s[gBZt@}y媠coicoDŽxfuÁzb~VsOvT[\cydv|roXe{_bjltrzMZ|A[_vhwn~گre~3B\F\pIMd6CYF_ux韭gj??a?UhM]kX^iYvbvouwobmW^Uei|pmfc[ZXhUqRmQ^TQR_UqiiY|_~atf^zTqMdx.ugLTdLRjJ=iD?[?LL0Fd3W];]c.@n=A\B@G/IE)UY(S\5WoFnmEKkk]|wDcV5PAOI5VLvT._0oKac}bhLيJؒgs{n܆hlt۲ȵʳ{rƞzrTMfMXZNj.Oj4]l.gFcmCqc>tw@O>Ȓ餇{a^Vg:xk9Mo7RX3mkDO@]h>RzCWU0\w0Na33R:8@pBt~Eooct[fh8lf8^wMq~IRqeYrOqEdX*G`D6F7YC&Qw/]y96__VLeG2pY=m<^agpLKnTXg.zp-MC`jÕ{b³]URj1Qz6D|.l'd{olWozGO~xjiyP[|DMd>?Z+OR&Zv6u;g“OYF[;c\-\bgi3j@~CvZ`KfDXu;jt)uEynޞX{|Kt;7S3BT#\j,J`[k5sFN]P;sp㏉PcWiAgQEDܚ퐟pim~g`ƾaf\O{4h}8DAi@NT,gf5lw4OvL\pFPx>k6{ˏZqPu}LL|]׉ۈ⋯|ͽqpjċ\iE{xHqld;mgQqb[am]}F`avPwgwo_efx=[U9peKoY^xRWW^d]xĄhU~X_cXzZ^h}ȯ҈}kitMVx:TzGyծarUL4/D!YM(Mk,=a9Qa2AX19S8:B(LF#DT'IX7T_2sa0p{8R[ZdfbSD_f@IxGX\;T[1V5t=oYPz^dZVg{ڣɩugbáԣu{}`~~SIe_nwHrer^yUfq=I[ȫlpkGRNSo3Zn0hu7uEgRWJ7|JDL2_d*ZANBF=Qw7eTl2u9T[r`Lw_`|DZ=ksAZuIJoIZ\)wc+tqBwTrVQeQj=sLUVOdx_mBޅrqiTOyVrS0qA[[8YBDOCg`x+Ec=da3FFŞ`d8k{gMgIo=bBwFn]k}_+lѓuXX:eY/Rq\WM5^1RW!`7tHiwzwDR9]f0NqׅߐؕjNgz@wGhspiyhGxOnC^f6Sp1X_4oKYmiqTrZkTorjd{r~[j׶zVWtnNg|JfrExeDkKb?Zm2}|H`~:o|>ӻq|bxYeLkƅjon\N`lLw`_vKan5xtQm]t^`ůw\zTyEwLoZ>cbA}wUfrZV@n_[OwE~eKUmZeZlg|vNuxRgWfHZS~O^xQsU}f~ˈƇxo{UV{RfT|[hqǾ[`Ya@/d+/^2)U)5Z5QYvr~\nUugДyCgJmfyQkRceegnfy`se~h~^{uxYh݄{uhtVjNPtG`ZeW^DNyDZ~O`YuPpO^vG]\o`jejkzUNs3=W/<\*=U5IfKnsڛq~YjSeUeF]ZgjvkmjuMZj7TnZ~Ɇr\os}q{grhnRVpMlrUreF^SIg|atcXX7dX8]X7@qH6ND=O3PE%Qh)Um=?qM4P87<*8F%IH$SS)L]5:_8GR.H<\@2bK;rU8fdCSxHU^FUnBlJsC\{[y]D}:fLNwﴎzoŽf}pr顎{۞͗~ưtt{[_wuoƚ~_BoCYN1Vr,Oz;Kg+YT+el<^IhdFTRbw7ZpGQs=BZAY@*pR/e]Wi^xuqv9hwEeg@e4әXŢQeDinqffĺ[zwqUJppA`tNLj?:h.YDqh+xsCʕWltPO~DtosZ|{awޕگurR^z{Rze9L[Shw=yVuCQ2>V,75$W!|V}^=`MW~DK{1_M9D?A;'UG"ga(Hn99[B9Q6.K&44C.6E!E),G7)PS2wN3oq7vZcPohM~}GoLjK{BnΫkxis]za݆j|ۭںꠍԝѝx}PāV{Ӕ̪p}atgfiPC|`?O9KU#Th-Rh$Qx9Qi+w2?IU\G^r1\p3Dm=RY05]4@;&H?oF%w}J\hggaRm3v]1sLLzKahÄ8`n|wj7s\zLS㡔~akd}V,ts=IOQ]:cV.ul7[snRlfqYfcA{M]O?pH:C*FMqM\zmOZTl;u`V]eEPMgwB[>gx?JuHV>*E];X$ifqkG[YY{.Ni'^m/\3Jo3V2U!qNki=zuWh]x4`j?ptw{WHE^Z^_qa~gdXm]kpZLuF\>|IyUa>uGhos~`t_dmRvzAqQ}S]Vg}ىٚurQjW{Pwǁ̇n{]HT[|UvU|ij}VmAxPuY\\}XiSs_ud]dILUJxx`dMV|AcMlNR{9QWNnpբ琚lgPaV{~|tIZW{y~gaIaR~xjjyLeR{vsf}jPa8[ZbnqTedFWd?gm¡ﱨvyU]qD\a6O\AavLpRdeos~lu[eLdx^onZ\G[w;GS9KW^y~dc;S^8R^8NTBPcM[pDTfAWkGV=N`=Pl[fhtpuχ}ZdVo|W{\w\[xi~n~s@L-WF+I]17Q;4G9?B'ZQ"Ve';m=9LF8F+EH-v^/j:ES?gD8[00L"?6WJ;u+:N;A5*FO(V?!bE#fk?PzJ^A/RyX{NȏVߗfzzﰃᶗ㦎Ѫśwpic_pTf|ġ̶ҤixpqFNa7>uPF^&:S"HKU["qt6}?j`lKW`Di6\1]oOw\uӒyxZnOrhQt~Xޘ`kOhW|d]~`i{dm^}pteSo@dId>Rt9Be-6R&:T*I^Edrӷљ\f~HqSnKjYgJUpRgp}qyV`yJurĚzKWa/3J;J}XttnY^BLi;@d1=c:C^|bBgRwY{Ubdzxrt^kUr]~bٌuړމs{ȲѾл\_mlTBuDLgiqC:zJHL-mZ-FH@mJ8Z.DQ&Kg(]j*|2^]}UYOUzFNr6Wo4[i5Dv2R1>n{e;g}^hjHɭW{V~jfv=tginLhHkyJIoG^f0x:hu^h^mCKIfdy^ey|hd6D:8kO)ƛJڠߪgxeybeMdMoq}otkhEm>mke[KJpJ+`5x7R\JKy{P1`O/sGaHk`4J\1_\&_O0F[,AU$UW$\v2RT8fvDcBeQ^k5ZC^}LxRkMU@Cd6Rg.ttEvPxrixm}mgjUIf9z\kW}FclNg;]Fu@ELoƒwgcqgyiRW_X*dpBx8A`!.R&vXﭼƈnJJ{xo?Yk3_d4~b=xqHoR^anKԬԔձΉqjZFi.k}^[骅sb}do>fODb/gw:u}ItX{Xhʐuf^NdicpNnyvJ\1c/~IzTbɽ癹}mv_akm|cQ~EiE͗UzHD[ōnqY{QH~tDjh:l`>NVqKnp=qHj?}Mz슓Ҏ㲍\r:vUvV[j:|cStNsGQljքݍºtˆi|Pu=o;z|NkwJot8oAleYVovwevHjRnO|SWUzUX}xliskV{`XW~2..'giQp`wPxS_KdxQpRnEetUNvPkuPei=Vh4Zx;_HpUfxsZpXl]}]Tl9Vu4Xj@\}AW|K[Gc|IT~^vqvWbfBUbDnS_~hoXZMcizn`mOiR{stp;B`?búpyxdt[xow^_O_Wk_nKJzGSs_va}JtiDajMwstj̢Èk\vrpthkponKN*FV08]4KL1PX)NW:>^7BU;YT,yc+}z<]o`\u:~>?S5Q/69"V=qP*Us9>~VF?FW<&Tb)lh3{CImbS{jcL{n3es@xUCmjsGFnVQ#;I-V wD8]4KGGq!&FIC[X#n5}qVy˅Scf|7kANx9W}8hFz\epxdoP_YFP^9u~H}cw[jLYZI^]mRt`ownzWcL_Os\[kUC+;m5'E,4*]=Gs/2cG[}6pFj?bRXk[lxPrHVGni⍓VglÀdV|}|b{YbC|l`rdm]kC{M[mvbo7bscl^8[7li٨Tne/T0;0MS>xrwGYO^)Bg@uk2kwvȋ>eY@G!?L'x_,IlFX9jM)N{DJ^,Zg2L](pQ5_8v@qn@fP,wnNzTSZ9vGvsqY7}gFkPxDž[ayietC}R{Sg[qPceyhvNfVd|asrJZg3jf8xWKObHޔ惗YYY̽ѐӁ`^IrI~SoFWZqZ[ab\l|}\aGrVmQymјˍŵMmh5x}C[v`sI\Mң́{XYԡnfufLtw8Om4YGi@tiA}Xû뢪֓gsGe{zOp_=Pf4L^?drNyѶΓЈm\{Ol]nq[u[lzNwa{Zt_uodX~\|\vaNsCd]{OXASv>IcCkpylpuEBW7KeZwlv]eMayRqrexcd^unm^|SHm}Iu[UzNmM_zTgYhIMk46>Vvwʻˇzjh}htat}MhgIptW{[|dݜ˒~~z|hIH:AG-=H0JG0LB$CL)3C-C@1PK';R'QQ5\b-lpMwwa[k[zm]Hquv~TshJQ>YV0m4Fb_skdk^Ayj_eBgLe\[]XAb@O|^m_n[]KjMهnp珆dªz_{zwbjʝŹ{{߃֪ٸe|Li_ZX4Pa-GwED`6,b/!F*.3WB:s4@K:6]9?L*SR,hs03G>>2DT(<=+nJ&MEOsDhs9>zC=V8GX!V\0bn9Md?jb5g=omM\z_nMs]u/kkaWyKpNYbdq6lm2\jC\rAOW>_a+z3^ͣn^vzg]iOWj9V_'Yj-SsA?}QofjV|UaeUI]{8Lu>Wu9c~HyE[ՌCN"K<a6T\n6Fh7]o-@]977&I?uO(a}]I[Bi,z.SsXDO+OL7ʽlÐ{~HnLvS&yUWvI\l3jx7SwObL/o@s?T,#S9(6B<zz3ndHzWG_m0P]-Rl+Gl7~[&kwSpHLT;\T+ΉQk2k1F.T8sVt9|F|q]VCQN-Ho+u>zCɀGLqeM5nc;fRa_x@`Zdc2rbәqnn|{fUqlen=dX7lQ3|[6n7nH׉_fImFxIQtVyԨாΦqWVuTi~kUdT{Tᒔl_>JE+yYiǂqńuZegE~LwYqVRm(BI"S[/p@UrKoKacdUqoMolt|\ixcpE^AuVfFqtAnjGmf}ȥݛԦݓ€mtn|fXu@Wr5Gk2Oi3?^+__[pnry^blzLxG|sKuakVuy=duBvS[OfMo^|c|[{PmKtZTmCaJom_f`jZ|ZoM\xO]uFZh<^x`sf[Zn[hbthvTdKYF]\T`9Mj/Pk6TrBTzSkWjy/;A1JePfi~qǎo^Q\lfxTasVgxYktYl{dy|¹ͶĵĞŔy|y}hPO@@E*A?'KJ&FD&;H08D5B8&LN%XS,wg7xBr_mzbru^eemar{Lo_][6ENLZJ!sf6~GuNb[`loYd>t^DsfK0@{,KWEQ\(5q54Q*SJ$F_)Je2Ki6de4e]6PTbz\kZ}AVe~AV?Vxs7ʝZ~V6cI>G `d*EzrWnek9KqAEH0VN'Nf?p4k~dPIu6a(VOhm/Tw^[=]Uoo.Gl<|\%MkHKp)Qw.Pi+E~=c[6MwAOh8_H/ ^<#FH(HF/[K,RL0gM/e7b;BkdLk>WUPF@IX0>cDQP9om=FrcAXUZS$hCexZ|tI_tAieOpfDOx}DwuKxxD]>iE{KjTmtYdoKkiE~tB|XuZ}O{OˑxАpx֖pz~}eaRgRx]yng|ʨ|p׬ԅrVd9[PC7}OR}%vk/0>!a\GH\zCU<1[9YD$Na9JE;I>4FP9SO4Kk5nW(rXSuM7V@]F,6M(dk.tJmTu˯Ie?\5_,jobpUjYEOIg/'F-bn`ko?n.y^+jw@}?WaYK0`]36e-AX"HL xd1Ji>Lq)Rf)~g-ujq}c}m~Zphz`dokrbv7SXw[z@N|/Iu*Xw&O9\9cz8Hhd]g7gY:sZ;^kHdv=fKkFdE\KEgfAjAg8v}=YStE{X[hXk=Ջo~]VvqʯTߕqXP8; 8)6/46\1xqlg{>wjHU[9Q9#hd6Tm?Wp^BwY7tq}MB+kpwOdRGKtBmbUaiw|VtXuibvirR~LpFuIvSoI~XN\ʞ锑i[qJTFŹό୴чnZKyXfmPs_QENn=əwӀtm_9qCe4pK\6My\ԔyLSZ0Z[DsySZWVy]fu^Y`hḧ́ˇƒmtmmON_;kyrţ蚀bzpwmzMjgQ~ZzeRZ_/E\Dr\dtvndtOvOrr?XhCl?`HtTz^sxъVg~Hs{QsOcvHclDp]qgdh\xPhxBZa9aZ?eeRp_zbySv\o[d]npxlr[nOjHlQ\ALi=GW5GOBkjd`tVn{unpt־⼁tte3::D7#NG"=P-4L;DE2`K(lc@u}St`|__wf;tT2XIA>:S>!b[.nEQlpD[iwur1RoBSx^cpKq?|Gu{XysZ`JzmRlBP`P^zd^fBxi?hz:nhLpdJpeNuSIhD^@yOƂifhavufYY[DVM_LW֭pюз澜{_gاױba}bfK;fD89/)j,ml:]yY8gT8FMZH$8|ZKd1IE9'UG6o=$6/=$4>H8/Ea&uD&G}B3T`5>'L>%UZ"-nYC8 J` Sj+Ug5ZV.Qw;ap8Z?tQg_xdhiuUiw@Ydn6PZghAMIT^;h-hf>JlWhj6Hn{xv]l\kyGjXlPhFS`=ib47]%UI"Mh;Yh.V7cx3M{pi{`b}ViW5pb@e͆z^qXvQk~iXS>Ir.SO)h9Gg1MA oW2L|s@cBqIUtzJevDozEiWvNHr\~>axFN^-KKF> \l+?wwv{yc\qxFoY|y^Ek}ޝLOfȆdnLPHSC~T{ᛯ}̃xqWr?[D265SJ&jc6ޥUS]W4JpNi1|gVlAhfHbv;{wHuz~hWguRrvV}QoJ{u>~LUMtJKu3neGnµq\mVoMtFnMyP|櫦\nIm`MNhÅ~}ToeGYGY|HUjlE7YCK>7YQ/joBwQblWdm{fq9cwKeyOw~T|BhP|buugeKsN}D|\{wVVqNUgCY\3O^1]X<[XAga?qY6riBjFa8ZRaF{RʇYԍw~zz}^T]EoTSaz٢ګ܋Ȫtˢ{ljїɠWcq62JaJ',s805!"#,0%2*"<)TG&nt8vlFS_C-UF*#;"QK[P5Aa5;=&%C"F-!3[#g8WS(,[X04!;4gL 5sCLI,r-ou/_N`~Akp>qFBd~΃}jrkj`jCaXVxKq8OZ~nHbUSSLg;Ha{KaXYuf3pBcNa^HfdKO[MPI-P_3Ib0kj,u2K.RA?JQq(ib/TJCR/2O0>9yg)|TtCeXpW*mkCK˗:\MM<%t`4xgqpیŋRpq|\5zRzGsMvGgIyVkZT}FTe2m>[hKyn7nQJ^cQ!Ig:X}-et0grDUS4H{^e`ZaowD|nE~YM{O\^3}FdTi[MI|2X^0n~7XDgBkNz^iCwLurGpi:p9vKQ:tQ-wco_iGV;S~@UGFd'GV#\l'U>iP(PiiTr@aiicp_n5o}D\ejkSvJEO˃滹p偆uzbhymnLM;Q/~ojRz[XyDd7wO[spH|cG{N|]i|kzcurfhBr@jQkv@kXVgFlrC}b{XoPpG][ċ؈eD`v:CU*_\2hZ9j[nu@^\gg?xgLf|TuYfft?}LsAHIM{;[s8Qp7jsFrO]|ZǹyřVJTbδsWi2LQ-D[59Z3JP,Ce+OS.bN17s3,>:&6D/;f"_L$[{8w_1_r9cNZ{?q}8Q{tp|j{ooozv^[S}ao?kTWngfwKxObz`ZUN`a.EU7RC(g[-|Fz|HJjQYm:Pg.9U5:@P"Tq`xeRNLk:V@eY=~yBXX5H?J+5V-?\&Nd+@]+qg$:`Z|@?g+CM%Qj-Wt6=u=_/~Cݧ_EukP^4Qp3\lrJ~Puw`bFkOkWom`OPz^KG{Nk_:uW:Rh`zMtgo|d`bfkAbM3~~w|pDBe/Nc4XcBsn‰wjsqwgmh䲂|VdutYoYnTx\[~gcNsGhAlMq]nM_sE`yMgvzJuZdJjIm^zz^oJUrCRtGQsEVuWgܦɂtZsYqyfyg|^nVo\wj~bw]uM^m=XpVmhjwkiSrTw[tW|`kXxdlh_htp|؏qm]ihjl`5_o>PQVdSo_@}DSncMyIlYrbCy~5Q_PoBxprJHF};CXM_K9UX1hc=awHXpOqhBh~=`|M_zHXw?gkEm`7hiAKsXXhKf`-Tg3Rf^Qe[FGcAwk.kSesnuwwogbdKnXw`3Ke?skKR,WJ0Pn>TkERb3\S4_X1uz?dMb\F[D6]}7PmGBa-]S*lO-Em{vKmNgCX=R[(s?wzހgh:lGMI'`y;eu6t4acFnE@b+JX&a^,XFhO2pOzDesM^PQj7f{@WpHT6X}Qj~z_ykS|O֠pe_;VS_tjp~9sBKVIlFa0p[,|Hn}h[Æ^lSUo>Tb1tn6xLdfzo;zhcQS}lRqFt:oե}wQYM^R*`r8bL_{5R|B]u3yy>pW{PuabIyG}YgwjVhQ}2_6Ni0tm.qEU`JXMZe5nVYN[hLcn>Zxfs`JjPg{PBdWeic{?mKWn9BV`K%Y]__]jdeԈمUWsrDʎ[Ʌr|zao]Kx]uWUNfOz`NlP}՗囅aj_yyYrLoDLbCtFOnO}XrDsTXek~rfvCqKfKfNRq6gr5f:k|AoToKkcH~MXm̋t|QdskueiyRtKMHPRD>uu=~Lb]qΌ恺_c|JTdIaŃݔԈm|TT4Vk/^u4os.`W,b>OZ[wgvIib5<@)64'f[>P\WzSbZ}\Y_cLsqD]X07;$A6*lV2Na1U`<{}]Vec:Xd[LrPo[`ogkFv~Jv@wtQebaCic>qI7L,uPޕy]^pZZ{]4qqV^YC`bAfT0xH|Q]SRMXY[Q*hW5|p\\TQP1qj5^XV[5yT2A\}c]jCLg5gW+l`:^u?Dt;_],_l=i:viyqKglHV_~`Pju~|}lgg^Sd;HX}E_v8xw-x_VfnbT6YJ0XF4j\BtE}EWSD{Bw|F|W[foAjȅc`wcKCH|I}R^ZYuFNTlMbCf@a=sHtJqKW]s~@]l1txDVUdwL_Q^c{Ya^{kqny_]?B\5M]7Hh8`u8l`m8U\7_jNgwVPb:cUCrdrÏߋ닚yci`xWflB[nFq{mnsbłzqql`vrx{Ԫv`y|RcpvĻ{[yJuRrRnMmw>`b/Nd6Ux@Vz;MWrsшxkxt~}nzg~ue}_zZ}TlMuqKuNtbd}^ri|r}crPtNt\l\zauezydyUcXqsmlZGjxR`_qnl`uM\~GUtJevQa`\j\{SliUnH`bx~fKbR_XLbJD\7RU3u[)c9K[WRJK\1lO6oh9rMzYjhVV^yMYyFZuIPrANoGk]3ai,^vMsl?kFk|K`~GYwFLx@]rIt{:frBmN{u9f\kfWeDi8}tDosJwdMyrDxVtIOkScEoHoWs[DnzwwzsowSC~`-:?0X[+j|/Didi7>Y=SN!=P,D\.Rf-6g:5D3nC!D}YbjK{ETc8jWpAk]OkWcP9WnGXh@\M+Z|4~B{Y\NicD}Qgel9FK7.#~T)VnoSs_wS/jEz͡xtvZQ]t:Nq8n5Uy\PtABǎn֨rtSHl?iv8hMҦN̡zfHrvTPJ7^3\ogWo[sɩu[Ns~KHѢnuyyH~^Գ^{fT~pU_£XwjUHUE[P-^ceooV|I_hDnv:jwCbr[VuiyXoVaM[|9R>kt:jgieCezDJg>9]6`pF~ln{RfL3.B 6F&ZX5cX_q>[nAPo0NN(PJ)pHXlJqVY>=R%G:a;ΥwڠdS|OTÖ䬆͒rɼTvPJg9??&[N2pM8XNrRlP8N>/Z<.s\=tIgN_mRdi>qt=\y<[v3uMsDxGX^dKnFstDc\/b{4[I6yLvn8Uo3Eq/Ie4jY;Ja.O`.nxDyUhcah^USasJF6dc=gNykEhF5s[?td[ʏ\Hml>v^LtU~vK{]yk^gʀqYsIZ^wNVnITfdPVxP^w?jy7nrHTjDhr7vvL~ScsXgiys}XmN}|RȽ~fvLxL?o<`|;Pc6hhG}bߡ࢘׀iapve}爬ȁaw^hlxTuKirIxrbtԄўϐĠ_oâܯ|im_u[y[cxuVvMmKw|S]cYkHc~D`{KhTst{oubj}fyfe\}M~CnzEalHWpZg|[mWj]x~XuUt]pUizLjtMqx[~s]uO~uZx_~mpojgKyGezWoru~itTcP`?Ur\rbLhrCdvKfsO`pKTsUYiYPdI`c;Rf>TnAlZ=i4tHmcXiJXh@Pt;fh@qXb0We/s7a̒dyRgNlGEd,Vi.Dv;0QEoA-J=CeRfb4b=YSGvH5]:WZ0k|AZV-{\2qNcTbnIeZ:]Z6\S7`7g7I{gHC6^>+VZ0CmjPlJVJbDMR|{<^_{VWaAoM8K-D:7CTH!Ko6Oh0V_;o_>xDbYWiPFm2DL+XL,d~AJhVWH2Pd7XoIwCh^p\vNbnRyJqffomʝQswn}aBokV8\WBJAqG1fvRlTdyl^jWBdw;]v0wo<ezSTPZClD\k4L|~bgBe=jWxx?Rj^KxVŏZhtRn[i{DhUn7ɅTsy`[{J׋koy\njckrJsMqnE|Ps]h}F=RsYGbb|Hg:RE\q4t8QVjMj|kiXWe0_[3fv8HsZ\L\e'YzX;]g/Y_|Dks;PupvEn;GT,BU+6F!I[2Ur@DL%=\#Wk0yVY|]UT]v5tMwIfI]5`>pO]ĵb`յvo^8mr;mQxIe^ʆ`ڦŹ~|݌təkYtEIOStGdVxJTmpCxTޥb^zValE`i:h~EevDcwB_f8~NoPUh6[q6ez=pVdSQ\{0[}4r~8M۫˜e}⤠til\~JdhCjpQm\bqr~ēvq©ݕgxvcrpqYxMiLvc}p[zIlrAfWtXe\KhpBjrۡx~d|vycy^u^w]wapWhz>hxDf~NkzXi{UuUqYiVcOasKpyWv^vHauF\yHjvSkg\Tz~Vu[q[yy]jWkkKm^zu|Z[}I0bTD=4a?!eW.F`BlJ@Mp6:eVR?;hI,^`7R~Ga_Pg`?wrCaPnsTpzFWPhgF{nAmL\z\^w^`vIIW\_H_n2`v7`o7ZoDVa9Tb4Vg5Qg1XF;Q\3Wl=`V+pW,sa;eFnAv`BI^EXS>HN%MC(YR*kg4on;`SEhPEYCYZ2Nc4LV6S@.^M(gS,cg;ToBIO5\;+iC)WS7?l?N3.eL'g8NⅯ䇜ˀfr9|Y=>/JR ob&oAZ^eQ{CSOLSGff4QvGTkADq@_sE|ISd_TPouDsal{EحGjsVJ3is5cJ[`Gkb9WD^cCz\4rq?tEd?TwQcTLOP4ZO2\t:KY^b|Hj8o[vX^[dkO[`:\>[C5db6R&6R'K{NU[cn`/qD˜Z‰dÆ{batgT{VmoIQQep.Rs5fe,vx:lwBge7xHi@p}?vOywDcxB^s>;ՙӕײvouM[[7^a4Xk@^l1[H`{LcOWwqzTRn7w5w\oYfPP{fqyDdYRu>Uj8Pf9Vp:`AoNx_as_ZA|h=ud\re@bT9]5\ʀWhƮˉU[S5qg@oi>eOz][fҍ~ﺉ؄tΩʄLaqnyYz\wNRV׼slMs|Dkj9isAVq8foBdJZAhNeu?iyIze}cViHR\*PE(VB4Gk,JA&KL-R{WXhj_\~Z]Иl̩t}j[OZ[Y{}FymxwLXwUuٻۄesχ`FyHmFiRa6c@zMx?yHczK\NN|SQpLcXh]bo`]xTu}LkPtC`m;au<|yh͏愩pkkXVx=`~4Y6\|@sNwdzpÉywyY[^Sd@W}@]WŎrp̱ǎڞ|h|sm[JjKgMxavhSeForZQmBsWNjyBqz\nUxecMyJhoFeqUn[{TmS|ahccjjfvawVq[y_xQlGcFcKf_~_sW}WNkQlxMwwR~{^pgy[q}vslxmOkoLpuV(\:=56TP&Dn1CV9hD+[d2AnMFQHJX5YS-gW6]xCnzVlF{~EwVWZPwRec?kj8j~LV{YgpYrrO_TSmX\^>af-ThZq8uVDiu:InE[N0XO*YO3`I1o^3rR?CO6FY2OS)GN(VP-M]3kX2wm-eXOpWSjESf??hC9M.@T+VH(PDY[1JX3P='}?%g\.=yN(03X)MMvYa5KG#P*/6"H:]aHc2WH9drxGZfJbOXZ7`qGnPZXFtdDjaGayRL\fSZNzc5_`IpOUwR^RlK:R/|soxnmU]dswoiNDEU`iA]Co^:PjAK_5s=[uӼjuj[zC{pQWnLh=Gɕ^}iztym[[|FF_x{obBvO_nV|UtXNkahwnfqoHjxAjE`?[yIv|BzUf?uu9R^2w[-{GrSkjR%jNvsUX4TB)f:͢lɦw〕Xqפҵnje®dηݲtu~Dz՞۸{SoAdgr8kGgUjVcSj`X|`ߵØnZW[,nhC|UkM^~Bvh8zQW}tKoh9jP9dQ*wsCDd.-740MU,cMzST؆cT|^Ito򗛦eiɢuÎuhiTdjdYaֹƇuf[hb]}S`XiCfCeFkGwVwK_@eISlHaEvNqLVV]wicRopF[NyWtdscVjOkYc=j:wvLokallaWu=_?h{NuZ~c~c{VvU}kj}^g{j`jGj;gIe\}f~`vψԞݔԅdޕߍ}uۄ|nXX`QtSr]imhUgQrKs~CjHsOuHsᇲmuEdKuZP|FntEhmGj}WXUvfzmb_rXeF_tB[iNo{[emlzafI`K]G^\n_}U{zQw~CbnJcuKjyPryU|ZVji_tU|bmevCafMewdDVAeH5sv59}O^SKrU*[c4qbEh[@XgBReL~]E_:D^RaNyf1xp:bSdpSshEskAuTq[YzVVVGl[U\>YR%dL'lQ(f4xv=woGo?GUZLcYYT,YU/UJ-_M3h]8]T4RR3Ad2O]4KR-SW0GY4gb6t2hIa_ltJe^=ZkKblF7}J6JJO1F_!U^.ST*xH*vY/9sM"F7X%\ E^s/^`5@6+H$96-Q%;B&4T!'C%O1Cm.ao=5[npxb^sRbkRh5TTAwK1U?MYK]L7echb?mnO}a=ojGaJVOehT>LCWcBad4Kf~~qms~g^\+\O@/0PE#~e-Mt:_9EM-VV#ko6CHz9R=HM1z`0_SDRHgC-=hxqiWgZqEhwHewMFzLOH+tb/<`_lYFxHYlMElFs@ZaunW}]FbghD\蚷鍓䙗z~gyT|oGs[e\4`>Gj=MW-n:|coǁX~rwzAQccքJuiJvPauzHuOe{cuzEM~aEt|E_j=HzVYgP\F3{Dj|YOvƉRz|e|IvU1\lBXj2[f/Sy2uzCyy_tq[rWzL^X`_ë́ipSx>uB~WemgJTpxZEW;pnC^vN]p=f@Ǔkzt;|^~L\BMgrGiT{ZmX^7mBt|VG֩|Γ_TggyIRj5Gn2PV+GK(g_?oSYYЛhÍԍvjwMl㬍ٌz͆gⱇł̞j\cnfqͅcvdSx[FuͫfM{F_υffjrM^z8jJռmjJdn9x|Lh{Cqz>}x;me=iU9dP7`\5EI%;:48>:#|^qIUig4MSaa΄n_dذewۂedbgľcZaNXOUyaT[mGcm3nCFJ#_N"gT%W+d3HKd@bWGU/KV*DS1XI.^d0hm5dq:P{GDjDUV2_Z%g`1vyEbDaeB_oANvNg]@j.tCC_-aO/A%B(GP]S){Y*jb0Sf>9[4T`P4UO[XI\h6MY?_G0Y-Sbb^[}m>kO|Is^jSlpmaZTvKrYrpY`cxkOjF\UGN1gcO_[pMdyGEE2<2f7?rvqYd͉Tcl{HS^WwIPe5ee4Nx/L]9Pc1uc5NoOdn?zpHYJsHo`hmUz}BCƲd}gkfJS~BFZ>N^$BT/xz2JkXzN_>w΅вH]hwgs;dwHsZF^P@sg;sFwYXf{BsqEhE`p@fu6|M0H\B΄^^|TAvsK^pD;[)F]%GW,Nj'cn)kNKwLrf5eoQjQ+|^(oBNZ[clvmtFenNerlz]_xSmd6f|?L1taIop@~TuRaGlEqdlzg{{KpX]sG^uGW~zUtWۊQŎ~px]xRZw?tCh~PftBMl9rh9pTYrnWۑrga`ިk䨊؟ٸȣgbc_jUbq[jyOQTq~[Ƣkfqs@wrL|NvRk귊Y{zQfϞeBkV1dm?\o[q=dpAafGuhAhX<~c>X=HA%ES(5I6HB%\Lre)os8ikLVkIddFW)iO#T^%Qp>io4p7zCN~P?dK5S*F8&LG!6c%;O*FWjl'?fwsvm`ujjk]c9kRKN>M`/\g6ob2=ZWgZCaq7Y\OUV1h\9[5RPictxa_eY~Wj@kILPMrG]r=Qj:\bE~i>GajfahRcJ^dApbFGUPnKI_4,V8A- UB a1uWdWhrX\T_L~Gv\eif_[WnBagAm{OtGaj6R_9`oQchkfn}xdnb{gt`tPluAan;as?gsBfyJo_|Xv`p`s[t^fPhWp[ee}WmQbTcQxVzJf~F_qKnmNruQ}[wleZxfusKZ=BU62N2&>1-=%+:A1LAF\4IL7eI.bg2X}OWcXd\DvE\qJfoOh>YLbaSh[7gl:otAqs=@SbLnzJa:QR-OR/CD0HA/V[3Yo0jj7qp@WrCjpQtn9az@k}UKLtUQt]=fLMR9VO,EL/5F+HF'ZC^S)Yq=Ca6<[Y@PyQUe0Po8;a76J./B E;DU%N_-Db2ZM#tn.zVawtj{cosymZfRmPZhJTFPr>b]1~h-YH~tMW]Q^SIW6[Z4h1H̆|y]}wvbfN|OVx@au@WxKXyVcNJ|MJ[3TVSAcc1HHBQC?Q-,7%.4:,= HJnVuh7mLxmPyWihOahvBd{b_0g}Ik4fE;8,b"kC|ONV|CZ>X;,zj6gnW]:Pamm_;xB\ы{nbC]qNv?Rn0kz2Ol2JW.?]+hW!x|@q|HoMvpHMS>Y5댁y_}sWrUAgtIpIm`JQiHGH9kT(sR0|MyVW}IRJ*\J-dK/O8Z~ONLBi@'yOR|PLm3Sl5Rc1];+oG+wgAbCKl8YsHTuI^tC^PVlAr}Ne~Q]j`i9bEkF\uģowjvpJoFwJᭆ~kizQx\‹`|womBuP{Dk>wDvyGyCjxEiyJyCTeT[[{NO}gcR~~Ǧyřrk蒄lƯvrIZ^[JhlĤiá]ᑶx|SsSwjBhBg~TKrZt_޴ɟkcgePiwPhҐԅJqZ9}gޠ͟adw5nv2ȿbtoaAn[:UZ9UgLj\pq`[Rs_ovΉ˂XqJdv=Zr@]pAdv@jIv]f|_lZdNfQpStU|ZW[rMzNnKnNo~OwVrRiqLpuSs\x|cyYhyZWNCAU,aJ*N#cq;wsUofMhJRyvWtcMW^]QWVL8TW,Zb2^2~5S}NzL\B[S+M[*fM8uY/lj8`dB`a:Vn:Vo8YmAqh5|@]_wUVhLQU?P_/Wp0fl7RO96D62?$ZC gUg[3fW5Vn:Sd4=p>ZM+bi&QnAQcE\j4yKaxyJp5jSND*Do/?e54X):BD<=< 9@,0R+I5bUnl7u{LgQs[O{g[zPh`TW9cT/btBVsEPe8a^(f3‰Gxqwz}orAD[s|fukdRjiI{]Wa;cr.XEXjK_w?[Y[V>`ON.I_9U_6RvOMX^66zA6B6.L1/,2/:"O4!P'sFdoMgwYWIW~:pY9cpE=^4W_$j>rQ@Oh@ag+ph9jEsq~~ceTtc?`}Cvf@asK~aCksFRxBm8`gc[Rr?ce?zT;V{QiXZT@uo7tTNl7dUlKlg)aNTo7YoCZm@jMr~D;LL';U-'P3/<(<=EOE^ DW2VH'yO%Ok3YaTgTCb>IbkQrUO^Za]hH;~`0lr=XːZuR_>i_5g^.fe9fp8__2VJ0SJ3eK6x\9E~OVLfKQMBF3KA'WQ%if.hp9@v@I5>8>YO&|N_r)PlD\X5^[1Jt;faDl{0hBeeOalpEIPG^WM_2NJ0Uf3L}AOtBXr/uv?Gd{~J]nrvjK~|=kq_PSRjASg/dY)Go9Y[?hw4g]l]M^YdHNU5lS6cm6B_AUW4or1I>Lg=GnsCFM?0\%*:2(2+]8!f_3S|OON(ug7HBlWA?U>!jR=dQ+BJbg6ql6E}KPd'}t0V;Qr?9VX`SB`{:OQMLHh=X*nf^}^xZI_cmgAW|xDjS9V@Y!aW/V^=]~Ah;URN]6V|2U[B9Q%pR@Wڞ^ntmmZgxKop;grB_jC_jDbZEQP<[N0jf8keIcJYmL[Q1oDe[^B_C=3qW-HZtLoCY9gkTZ}<[w6[~8pRm_]Qo>p]R^3a2hqOV]h?i[ibK{NpcvtG|UjiaXP{@58>[?Gj,8Q>>.(RA Wb)[cA`hHs`Fqi<\cGnZ>wqAaRdbPoX?a5giBd?=E\oWO2iW,o[,[`:^Z?[k=`]:]_:ZQ6#JS(8R094'@@L9GR1Hc7T_"ek4F{!6F 2=(Z/D]*IR5bS(|>`i+^U)0!:0:9Y6$Sd+Fo7Jf3Ze/`7KtE_w0zr;Q}ywkv>wx@sEZSʃ|TkRJlIZg5Q^?T]*q+wY\oLrj6|U9n[:AD<MC(VU>knGtdOLaKDN,gm*NvJ9c.:ONLnc#ƏMxzk]zkAkU^rpeqT^XsHcRa^x[spu_xY`rOQXhBygAo_BieCmu8kjCpeCyDڅtPNsi\zji7`:̠aWj}[jUVd?|g1|KsXOaT]d9uk@wXvmIxCX`wOl`z_~Ptd@oKkfYQL¨mxcl~yNnCh{=cu*~6b覙{TsS`RZd˅i^Z{`gu^jXpġݵ˨tfEzQdʷgPd{x}QwG>yscP]:jYEX`?TD2mPՄӳ剚^QU^PN\~HVNfCdx9oqD{Y`c^eq\[koUYqZwOnx@vzH|Gvu:_q@fxGo|WzoJwd{_H\f`Ԋx̮r]OoG~}R||R}҂~rY_Qn`P~c^~P_vsŮ{†Ս׉~m\KtTgtd{\jvKv_uZydpSdNgPqX|eݐxuzntZiyCIoPyqugڤΟՑir}UzrkUtO{dqgvamRxQXzP`rAC`9HX=\[DkiNn~Iq~H[l8]f>lvMuxKpKeTpf}v]xGd}=_{Pr[yTvRnRsROyvEep;]qDb~VjdkfWw]ydTwwX[v_cSxX}PvmEhmFhsQw~P|{L\S1_b5KuK4Z[0<895 _88n038HH;+`K"br7mEgNP}UQ]EhO7im3}hBcyJV]TSQ^]1wBCf[y]gW{Oo{?F|Vp[nHulG_sHgnG]xKRrGpk@r|DhUcVUkU|c=sCIQ7Oc3UrJcEYKVWfDkY^aYX=iSBM)CG$WC!2U)Q8&[I-VWA?MHZ;PG=U1>L'6D%FG$Pb%Ge1Xp4Ts7\_.Fi4Zr1z8dWhdKv3so9ZvFr{JtI^xaoM|O>Z5?H$tm'N:;B#|n.@}P4C)AD B@0f|Aaq>tk;[~GAPgI{Iq[VVmLtr@{L^YehƱĉl_[zQZ?\o.|4LolP}N}ZyOINzMrRkY@dA\BO[~xNqmЭx|oQncN~\B˚b~ĶldcD~;KKgjM?Zh7gpLuiwzRd˝ʆdeQUKgƐۉjq3MsmfPRdks©yidguuvΘpnEl?LvDv>{|EafPqk?wgMbWaKPXrqE^Ib[>r^OvYcZoGj΋́brXUSmpnCet7{PWXmoÞ뗶Ȅr{skTomVhLeO]Plh{qwgm_pfm]zZ`˺쒉swdmttkxhgjq|wtw`]\fQWw>M_2Na>n_aqWYsHsT}[nN`zHTg6KJ0DI9QnNcYZM\@ZvHZyNSqOh_mWielsIvBjNuao]eLb|?Ri?QsM]Lc{CZs7Od@ZzHdFgyQtgqvrXqsFh|^snV[ZsFmmHflMp{Eql@RN-TU52\B/DG18*C, g>#Pk8EYGYN9Qk0Kf>pZAXm8PfKW_E_V2eo8ekHUsKMQGM`XD,wU%{4XzfdqP`oEtn@u;z{NosMjcVhRAW9ˑNulVjoCLF,;-<*"D+SA!]r0qs=PmFGZMhZ5[rgoV*i2/0K3dN&xAxxNE}J?L;CO,@J+K;$>T*EP?]]'\~4T6Pr@rg3=iHDD?JW'^P,@l8R]A^]5iqFYEoAAulkO^T\fNhrAuEh~OfZ=kp=oaAanI`gCr^;Y]8]V;aZ8fqRK}VPoTO_9Pb1N_9RK9YY3Yf>}o;wY`fC}R;_EH\)KW(IY9Vd2N[5E]?]Z7u>Sg@K4BE4GG(GJ6TH%ec*fc3wv9PJW{EcIi]kUdLsE]=oR(ylC_Q^I}xFtxEtXmUdn:^b?rT?'gk7ge9qt?~`8pce]t>nܔχĭٵvkfhfZ~xDj@e[]^cf_VXWgs]OTs~:ITvPVfBcyNkmhhQzm?jUcUNvMzRonvwWº`|`{s\kBUu@tb͒}PquDxR[?pzF`sÆߋskon{yhb`Yr\zw⠘ݠ۔}vme\{[~~u~t}yØvzi[~Wbn暚zne[NKM@ICO[Nk{GfOp@cGmSjXrgtK[t>\Z~㠘爆qiXlbg{‘q{`tT^s8GS4XnM|cUhC^AStAUqIYwI[n)FM,YQ+b[,gq9m{CMqMdQGX=>hHUO@[,T`:Oa6RoA6d7NB@,O'.3!I?CR*YJ.\W3Oa?P]9Ee=Q^:He5HM5PK3HU+FA4OS7e8M[nTAQsIScN[h@ZoDKmDU[.Xj,D`ZJycYF/}:]uk]kFYg:QX@b[0nLbvFLE[g=vw2:\J\5T{9Ju3]f)l;7oA)6%22X=Ga)7S%EO%Hi=FG.hJ+gvETSPGHY5C_&=T#GQ(Xy5[u5Bπ^oeBsYarWhxBJ{SAL;A%VWcq\9XhQI6G8[sC\z~a`lXzEmcte_3icjk?UZ:b\/jsEZuC|@tir_PP_Wy?VQWy4p{>ZngItP}KdWQTN;dϔтʙ^z_ta]c`vǁ~ҙ~šѝr֣d{mFjX1{sSzQfMf9kN]KmwNSC}aUezKepCN\5aT7]kmPelTh[xvU}QTsm?{oBDh~eOb>x7fpenLTKjv:]u:s~?pzMʌeȘՎpUeRwFZ{QW]Td뺙_hSCZ5hYnK`Bl\nrZsLoZâ㨭蓌hr`yoŁ`hOSb.DV5SiF`FqKqPoM]>Ml:Rt=Uhe}PXrM=qK5R=`?&ep'b~SUh?L[6bP-LP2@`BGO,AW08C,KI$Lj0Tl5do8n{9uKa^]}PqBhSgbW[Z:OrA>R19B*G<&YN#Vg2AZ?LO3Vd2g`Apd:PDJwKBh;3P=H? NU*Hh9AU3LB+;b-?P-=E)AU'Z[-^Y<_`;RT8Sm1PX3V\3ug;_Hw\tTpcFkgDYtSbk7c;FYsQZ\=B>;c5wd)ZXTNqFgu_J:liMeEdڅˑcu\jDcc:UP-XE"cb8NI9QW8SjEQN.`d0`rF~=l|zPVѓwuDU5p^2kj>Nq?w=hCu;qd.hwEY}PSvboyWrgjX`yUZBfLrﰱ_ܣyrn`:vTBQ>fHp1X]?Pk;YM2ehB~g>cOvnXx]`R;nQ?{UnSrgIziCwQɧlwiEKrϗqd{Vthl~>iDhsҊѫ}kUu_Rd;iJXZuEhm5+PQ*lr7\{GHvM0WH+E7-:,D9CR#NX0Nd6Gp7Hj?Cf=LW7Yd0kl0zx5U=BrADJ+SO)o6BRsNuLJ:PR*_Q0vh3zLs]R}PsmarZHcZ:V^@eF9|N2Tq9^XC]V;nm=BMha]{mUM,P_(FN+HT,EY+Q`;aa3owHh^GBkFSVAXB#\x=[p=BF d,W]UbGLc:QS/dX+HjC@XA4I,;8(:J(;I6eM&W{1If:`F8sx>|\eQhW@YAmq[[[3^H0<"Y9g;IQ{l3ArTJ,bp;k~IYlPRqCNcPSd2F\.CC+Hp4z@_odJR?In>HV0Ai.4g1H`&^[(IRBII/TN5_t7Q]3W;kJpc^QSzAtj4aG]=8K.*_,]Ij`D.LhEVQ0P4_uդtQMQV3iG]hwtjXqsR3jKz?ZqWl,o\.PS@EB+^D#F\@QF,kaCb{DsDlT^lG@`6wRDjy?yizҦsa[njNa=QlbQ]:Rô凘baiƒUlUjJPh?Ah3pzH΢mٷߟzhŨwQdnpYclmIrm>[μߏgxD{HwDpe9gw8q>np7q@ySiD\x9i~>Z}=bq=xLdK~gLf͐Wpi@lg5lo:bPy]qPnH}cnŒxėsdej^}d^slxIjPjexWntqN}Rx|Qz_`[YXqTuᏛa{]Ym~KOk8H`>zl_qstQlPxkxuēilHfLqWrYqenmlUlJiOohiVt^c^[VpVtduyҶ◔tzJ_7IiCg}Us^lyeǦw{ĊԲ桟_qR{{Xs|XkK^|EUw<\wCWs;UtE_L]xE\RfG^nARk@OrEPzd^YzCSoJaoFXlKcezl|cnqq~Qj}R]g}zgncjPfSDj?Ji`z`Twb;v`<^M?LR]8Jd'Ha0F]4]^.q*}RgHm<|@NBYocg?\NTBDW,\P.pt;hMEgQGC2RH:X3JX3Gp1Tc3sk.H{mbqXQTOeSJFXM-;q;?]EKV/Hh2Z],5_6F=0BK";T36U38A(E<>[2=A/^A&X7{nEOtjXwbH]=@C1GY%ES)_N+sU+`x>urLn{E_hQNlC5[IHO&Hg/QP5UQ6;RTT3rr7UXFkNL^4F]1TE0TS+\Z9q\:v`C^_KDfE_G2Zs|a6cb=J]=89$AG%jV.}KWvRsI8J2QI#yEy[v_hX}F[gWc;s-aZ=jd=LwR^XȰyiY{`gʓjjMeK7afCWiidS^4*hP2j[2li=]B/aBbZ?h]>f@vcG{nRyQXfBX`?k}KokBQY8\]8a>eYbuDg;]]PP9LM1`Z5fd5\c1r^3xRlNgVlԧL}DWR0\N2sܬؤ|fprNV|@igxG}JzUqWY@yYB=aS.YM9YX=bd?iAPgqⴆlmo|rmMorvm`cdLfE]f>uu3cCLlM%J818%WIAt7TV6Kq1Xl4n8HWڟ?FGJJA'8R(3C.9>%FO!NW%CjC?M6DL'NR*0[24@*HE$FN&3?E AK(X_05p>eo<=ZPw0eEh.siOT\oi@_TK{@F'[he;Zpj>jhiNS_aڵ~Ɲzad\8rSB`<2]>3YI1dN8u~QbQUi8RX3TT49Z7OL/RgGZ]?C4r\FPKf3{FlaaMVr?FzbuL\N{=u>pCuftcT~VzQAZT<\D4^B7Tюph^a7iO0GR1Ya6vW]rIbe=Tl?^f4nhBgx8b~AxpGYuEPF(XNl͏ӝHk7I[/\a=~‘lWwolnUmGjKsPKQbuDWylldct넗iS{mrBwH\eכրa{[UwLqF`Ļ޲kh꿣ϖkP{}HqB[yWt~==IaFKs/Og+Bm,Yt?vOIW닊UTC`[6eP?^J8sM9cC5QCgKjM}p̗ϘŁtmLcz>PxJtIgMPrz9`f>gSbR~pbn|rsɯYriFdhN{Wt}L[pG]g;BP#/6#-6F!?U+BVCcsMkuOcj9CW'D[/Gf9f`j{Fb@c~LqXub}hmbtOjM|bbyOqMjZXVk[cW_jwmveyh[dAGh3Ng:YbAje}ւ˅ڏ~qɚޒciMrƋƢWn^}Wiarjwj|cOkm:\qMoWkObOcTl_}uOa|?Rr=WdFdh|zxu奬߈u|yppftcub^}MjOcUyq`nW_ddhbXlWQ]TZM:>^5T;2{WmcY{[KvOJeH9X;OI*UY'CU.iK5^k-RrSeFmK?1F[24/3:#=<AHUF&bg*\@eJ8STkYr>Vc@f_=Ctdsryֵ|n~i^g~{v\bXZT`NBZlJR{IAl>6S.,J&>J(aT%?9OaBZe-A&2*98NU$Xs8MxHJk=d~7s@VY9cL;9&=<"9N)9K%@P >B%Cc9FU-qv5oISyMF`+JO.];&+C7X)[u8EUK]<3UK9;Q:XG!Mr4ct6yrDUlO|=[eIi9BV-Kq@q֖LRKlw$w+]YfTAxR;{dCNii_~Mdz{ffslZsXk6ZR0ZU5SL(dK1h@*jR1je7X\Mw=T\3Ff?7U,1&@,?6[N)n8R:}:lUZ\Sh8vEqc6]HXAXAas6tp3g@[|qRgbiZ{Up{T^~|nn\QaHMl:qzHzLt\=WVOJm=w{MÛ{z{{_ZoqwsWahBtiDg}KFR=uhB|bcVOd8_w>bwGaeAbh?SWaMtg{|ՖSk[eCVY:a[8tzY]Rgz>}Uk{c|T~_dž}|ePf|:`C{HzWjQzAVlIlƑɂjt_uܮsbpd|XOYauAy0F7]B_uLjMsٞ~zHzjHkwFegGwqFgK~iLpnGr[EmTjGc[Տ~С̾`hr[gAqHy]XoIqtDpqIfyRlle~㿄}p]aXmnEutVcmndyTtyWel9476<$?P-KP5I_>]vRT{@Sj>Zi7A_5L_=^jGbzDl?Wi6Vr>YKx\tTu[}gsWpUzblfU}|Y_l״ƃtdfN[pI_Eh\vRW;Eb4R_Mvvԗ菗qryoM`JkpwXke{SnsYswl|MUa;NiVhbWHhvD_qKmxPd|@\h6Rc6LX;`{b|_uToZx˛ٍst|qlqdshzUoVOoGgavGMRt>@pL@HCO?0OX+;Y:vS8mx0xKYRjVwkJcJ;R6CK,HN*Bd3_O/I]+[SCZ3yON3Ja33/(9&.1':>QMy[c/m}]gF~>ret~W`mSXpzvcqzrb]sb[wHkcmxgwFjL\{}Zp`UW7fJ=R4FQ!H\$fU.T4MiWK\EUG4Hn0_S7^.ŊGK.X87WW"C}QK]>Lj9GoTn8sChVdajivc^Fc9zC9_4:Q#8Y&:G)6V/FC"4[14?(@3WK3Zl9XtMgUcY@Q_Z4k_.Yn?nlDJ[fqtuEetHapHMoFKXCO[8df>nN{eprsqjO\]]?ls4fMjuCwtTcX͎PmXuvGpZmhK]AhI:O+Fa)?b87Y8aW,k>diMpbF\LjUl~@`cfbLR=S0GA$lP%Ye0\p8T~CSnB=a:NL&@d5NS @=*U4+`k3fnMgM?OL>HE9bE&aG\J}i8ZUJS~\SP(F[)k? }`)}=st[`mp*wwOcPhG|iFbFsqEa@t{PmSyeMuO}jlB]WsTtWxPyl:g[CPCRR7\Vzd6^ovIZyOk|JegBnpAg~Pzgwgrjk||ppUK^BYBkyEmYsY^UsU~^{GfIZx;rO|Q}KjCjFtGJhVMzE`yoYJP}KOPḭmƽڀwլzĕlvFJYUX]cw_tT]_hRpW|[TrHg@aL[{[qEz`qTL}3^j;[BvWhJde4amBޒh{VhuFc_A}_D{T`cN}nGcVZY`sQ[_|lsVt|\fWBW~Q\P];eJoSx^|\xjrYyV~a\_}ʥvIRi9Pd=L_3LQ0TJ?g_HjU\n裤mhEPjDbMiJ_t9avEpd_VvXdKclGcYswfyXlQm}OcmBPa?mi@jlEdqDYj6Pd:IZ9H_=ZkIoRpBZv8VrMql{dzQrZ[c_vhz>]RXp`9bP>I?`0*Me-Gj;aA8Ff4B`9VV4oc.pyOk`Z`Ny\JfCIU9;R.EM.I^+UZ1A]4ZH;E'wGI@IaC<+5R-,D654%{J~[IzcXgQ[pXlDd{O`zNN|M@b=AR.FM$mT)pr5rJfT[sIKZ0JM,RC*Xk:ZT]V~Ua~fjtǃ[cSzTSyI`Y:s:radWwtxNqOtVziUO@jT@Y2oX(NQ0`D?H#MS%=S-CH%_R&Ru6ctB~9ooВG8rUJ[1P~6Pn5Ki.kIUx'X[:PtBL]7Kh0iU+avE[oJtf?Y8N1{PzKb:wyNpI9~G3jU?gI;}iAntM}|D{[DͪmbsYxakmJNU1}m9sWv\]e4\=~ɞDŽzÉÊ|jel]UqPVJ`l$wLgY^]_pCi@vKlxH~P@RG7G..D,;4FT Sf,l_,NHHXCNP+NW1;\5@Z7Lc8`p;gK`bjTqXbePzdIdQO^5_w@WGO_DLgeCCP*AX,AT*Hb2Fd1IX8O`5\o@HzB=cN/`54D,WH(WX:yFT;EN;HO*9N*N3'y0qrsqpl^ao|ӂnyGjJfS;\o5_zTL\?p?OmfrFl{EyPeaKl@Lc+.`24C'<:'0I*8@?S#;P$eF#bP+Nd6Wc/gu2m7h~׳ܹLJdDh=UNRgXpFa\?EVjD,X9hFvGUiB`9hoOwcDkL~\uZprPh{F{qEtGjtjGc[a[7dSW]>`2#}c:uykw{YghAGi@EV-mj'JXmHdn8Ui~eK5ueFrfAcvMULsS/kZ{R?d[mLRˡߩJ`.]s4Ps5E\'jA}݀xS_ղ蚞wqP{~ERVKo|lmϙ􈓼ceYZʸڛpt|mlCQ4jk?wH~qJpܝΐk`>Xa7d|?Wv7hCoIT_7Z\0dBoVkU~iUc^yPqYy˸քhdyfsMrJ^qsLFGXgX~€̠푪w}oue}fl˚ab}A{lӑƅ̗Ԇk~jlydjU`]qxz}vqeQuon[LjFjFx[}fĔ먴ԅvqmsĕܤֈqbw\~hmR_xCOm5=Z6DX@jtŌԖܘ페tdndKkX}ojTyKxKq}PpvIvsCkAWNhRvLuGi{JksQW~7JT,FT6\d;Yi?ZuJ~uBmb3Z`6Pe9agB\o=XqAY~XweKhu?`M`GLm>LfJgy`nCOOGdMFY>2R=JM1SX.{X7PILkdObCa\N38H-a5Cc5?\+QS0Vd3[mU^|;YtOTuV]~YpZcV\S8SFeLPc?B^5VI2f-~xpoPsVhdAy}GdpDPF/Wa!;M#@IX`"Nf8Oa+cn,S/Fȱٳ|^\U?rB\=#SF6^V9\q,`U2zg8sJkUp]mTcubLyP}{hZVY`M+wC+U[3xo>y\HqQLU2SC/mI)kpN[H]n9kD1aUSQ7}Q0NoXQ;M7i^peGkEO/^I)VypZWnHM`D9X/`L!DeSaE{{A?<.V1#}N3oDiHalvIU{jg_ugjVZf6sxP7jc6{{U\MXd-gAWauPyPf{Dj{JypWfGpPg`vH|WMqEcr>V=dBbM`L\MHwwwN^{ɽғƀ~pЋ׈sibxUVTFMR]onkp}p|nˋ߈oiubbg@slehwPivDZvRsBZI^xCE5FU7Kc3Wr3Yq=sqAqEaUhqM[\vnR_J?uXB`@MX7=\3CG1B`/W`9]nGi{I>~I'OL./$6.e,WT(F]B]Q5Yd2[n=^rBTn6\pE^pHeMdE_|@XqAdw=pDNHUuEQx7Hh>Pe1zx9xSOkIoHHGOzOXi3U|7NKT;PFSpBVi?XxK[r7B}GFR5Ja2;\:4=*=B$YF$Xw=VB_fHcnKlqJjzWbzTkRV~Ǒwi{pu`]GlgK[AL[3_X6wO:oTC}RKQO^;KcAM\5Rk/rd0CW7X7`R*9eVϱ;^DQ4*\29='NMmIeTWuj{WT]DR6Of;ri3G(rB{}dmftch^OZ|HyFzZ_ynpzYXizWvh>Kl_^kTUggq|OΙʃ}Ux?hֹ堡wwLfwIjsznRՙepYyŎ}TOxrqë昩wpgsQuLuqHci8h^g8Gp-l`7dvA_DrOPf8Yu.NkMn?xiNm;Om?Uxb~ŏہefU\PYI]NUzFM{RUjisvejFTf8Fk;5jGFQ=TH.^[2ZiAimPv\Yx_d~jLHa_DV_>amA<{EdB=LB1wO8pb5NxQubOqEphJLd\P4|0OwOc~eTCT_6IoKScEQfESpM^vCg=Tq?MqIK^:Z[5PX5SV.R^0I_5he;}FhW]_S^LVK8[X5Tf9OR3yQ7vRNQStPKCVkA<;Z]9OzecjVjDQ_B0r>074*@&>,K<1[2=>)>Q#>O(KT&[a'Y|:ZxEQw=[j4mw9e~MKjEWS8Fe5Rg6@p6N`1gf1uv8^IWkAgiBHL/RM&4W34@*EE']_1aI`O`^A_cG`[JJUCEG3YQ1ff4g8PȬj𗞹ѩ`ToXgRpa_J@[d4?iBUF9]u:_wAPkDkh6IY?kMba3^y?wMs?yvϨbɣLGeOX=R8HQ'~a0y_ejOJ=1eD2eSweWTfcft~7cuGe`8}YbYnWU2`V(iW.M4F3jY>`e>aDlIAqyOSfDh[=tJkwfP|^?K6gahjq<^yQyjB^XlrK`H^w8pwC}Nmf_M쇖nRSM0NF*W\+9d-LGHkqeXaz}\tXX7GP0wa>{rZ`K9S@1PuEGU\Ԉ䲫魞ܚs}~ZczRuK~iEfuFZA+V7nZkn@L[2km<~Prmy~USlC;O(6G"=W.=Y+@V/A\9X\.>b1JjczLD]243HW-jlAfizz[g|J~{OeQWj2f|G[}>gq<\=mJsu}WpTonLlR¥ᄚфjÂϦbee`\~>jlˏ}VuPjjlKN\iWyT{fjkUzZXqKTZ?kRfLj@iyAxIUmN[v6]}7t=JpR`tYc~ޏyjYPYxZrb{XMNWwVl^xa~]abg~<`^Y{7\|;YFrwJvJewS^{Zk|Pjn=OU=PfXxRnKZtERpIZyZ{ϡ퍛tpmZi[patguivxzmlRJf/\N7Mo@<_JCI6SF+]]3_XxaB;LZYTSbm?csDyaEBU:OI=TW0ap@{{PyQqobaSmXbLo/znn{Zx`SgNmmC{Ig^Z`ZVJp@G\DT"C]*@V&RW&Ug-Vu?RgDmr;_Q@`H=P0IM#Lb'Rj0Gi;DV1;Q3UQ-Zp1ELKc9[0BG5jgAW-@j)KQ4YT,e3J^h97YK1XG.jq0A%IN*EW+UP/]q:vBus{yRD_NaN^qͶj^|S`SN[uEryATi5#REs`kYslQxWPn@ey5Xn:qvKLP?A0HNkh8oo6Gp@]Z2k;vpMtxOvo@kpoușYyeRC56c=&]D5vbzHfLhf?eyQlgR\=pgN[C9dN7_`KqgBgfϋbuJf];pLyly\i`fMp>q}O~pIڈFWOtXX~MWMEf/]^7ŁOgf~JmsȚhhO~XsxpjL_Qn{>bj=frA`yGTvjeHXaBnM{MqN^i5[ҡurkaްʼnŒГט~vT^douRyq>hyL`QUm]IlqFMkiaNsMLRyLv=N̂cZUyyJ{iᡍ⟋ϵwel|uW~PyMfwDorCh~FMN{Ivs@h@pJW]u[ވցsj{kusibjiuFZ`@luHkbӌhXLl7YM:laFyfz^}UlQRB^vHpsl^}i⧴}z~aiIb|RrxŇsYj|9Xj;_LnHp;cpA{[uv`RvKyO|UWu>Hm&B_(`q5Vw6IkOi]|cmnA_l5Qg=czPqM`u?^r:c~Twbg]OuwPw[vM\rIjPeL[IbzMictmeX^tK\d7@Q07L9HZ=UcCUiRX{OXwFS~\quܖ҇|c|RbsCd{IbuJbhYv{hnY[{8aC/f\2LbC?CTT7e0=ealXrh_KKIC1=0p(w_cubwZah[dkKlJdShOWzKkX;ck9WwIfpEGy=H`=H\)HX)U[-g^)NiA`dKVk2UQ<_Q3Xo;SgEjBdNZoVXYF`XDqu=`kd5oV?71\<^Z.Q_46uQ=7?J@)SR4Ms6EcO>U/AL+\H J],VY'Mp2ubC~=FgKZSR^)Ib.Tf:Is;C^?Ra]BVWwfBz3ZqCIZ@Kf66gA1?/CBS])S+BP7SVAI%?K8M+Q`4=sB9X,6O+HD(ZW)~g5b@\Ӑzrw}>piBq[9OGH%Qb%qt=Z;mLfVQHsF[U?nmgotKP|KGj;W]/\R&IM2K.pgn[tMhf7EtgjZYDGk[IplB/uR;_=W_L8wi<\l3st>MTbxYV\D^L7psS~I}mt׉esSoCT9}_qdvSaTcM`䛽͎̖ͥeZyQacazdK\Knu6qBfh|cZѕ}Wrvx|N]_2k^5uɥ_fqwq]T^?kꗔzi~fma\VwT]enrftLxxCpHd\_ltʍ|tV]U`{LTm@Qp?^T\?E#Ig5BZ-NO(]r>C].gU3de8~K/suBkPuJ^t@I_*Z{AUlY|Yqā~XhtpfUj^9Qf8|Oyw_~M^ՒމkqVdFQt2EQ+seLiHzScZY{Hk|Ei|rtmRgfMrfGd|TtgΊ܃Áֺqvݰᚺ牓gla~Q|dOcSr~U_ŏzehTP|?LO3`P=vxNjMWs`]Sgctkx˸ȢZv|:KV/bfGv~Md~HhQp[rOV~9Oa2_U4^V6WX5SZ9hrC|LwGg=\t5UeK~uTUuwSkhLeRSPwz:Gi,Ol7eOwgcxNfQvYJWS.JZ7KeE\tMj{?Wl0Ij?Xc~eqPlzGnoIy\Wp~Q~^ZdpiqşЕrf|XiJ\GxTHjMOlQcoQ\hD]w`pqhixa`bUiETp@Vz?Rp@C`1@C5?GES\P]aA;S9FF4KR5CN3EG+\F/dE1[O8LGAYH>Yb=o<GefCvc@dH[F7LH.b^2|\4fJnuZVUY-GA/+-Z1Q f9ImS_BOU7XT4Zn=\i>F\AFG4pF-\-]fEhxQVk=IwEMh6In/Rb7Xh:DnRV]@Q^0aU2e_6^rI~QLW[SdRA^;fI1x1jtugo[AiK*J74H.K8EN'vQ.F9PssLf@OX?]Y.8~K/V@J@)VN@a*Q\/YsW\Xu[Mk-Oe4^x<On;AZ97Y6MH$Ib*Lc@NW?KI6Yb6Zv@^X9AiFZS2do2Tb9TeDYxLe~PVNkeMt~?`bUZlGHx}GQTjo?sDQGIaD\k-/U1.0$O+Pb$Yj5NxKcOMyBKi5VY/}v=_noĊ~ipLlQ>Xz@fGeFzC}QXoπriNYXmrL`FEa6B_$@O&IU%M_2rt:i_|TWFTk4gtBHq{gXREL2uVZrDS6e0 L1#S4%aC*_uBtl5_eF{^>?UD|ccroO}\ttdjxbtkp[QviXYIRה|hpxHv|aaWdvCHzAnj5rAt|mz_DTmc;_L]x=sIXvMqw|C]}Wi{Uq}Mf=^Fu]b}_d]Ai_lN슺cSh`OUNk|qy[fgjwWnsHMt}wtuŭwuqygpsFn}MtVUTmi?G*<>!8P,+>SP,Xx7W[6ra:erKfJks<\tuLJoD^q8q}Adby±©nZHziIkZܡ⛺ӊ{rxq`km}TxsL}a_\mvWi}N]wQqVeX[sSdYtL^fZmZrkzs_u̬xwţծ΋ehlyIMN/CL4LdF_|K`QgVyXqMssThRLi>Y`@Z^@j\:`bB|OnIkHi>_\>rqTSv{CaaL~mU|VsPqUcSe5guFvakUvyBdRtYue;hX3QY1K_@dtCar6^l8fLz`ziIhwFrpEuJxGjcBcaEnnPhs[rcsbƷշ۱椮ސ~~gtZaQPQn|``>MvBPxMc~RlQaKgIbCRr8?T2?[@IhALh3M5>H5HB*KR.TC0OC-XM;UQ>PM@H\7tU8K{6P\rVJofB:iN;SCDa=]Z7dh6@kQ;4G&=,0.,E=$\I!b_/Z]:R]:EV6\Y3Th6IT6>A,F=*_[*Tq1S]B^^;[U4OQ;^k@S4XiBWe=Hr^JcJH_1YW4ge6~GvS^nYGwP=X=@L1gM*u?Xe>z]=S.5O.a|RShJnAdVaht}7lHp}SD_>K>BL)FQ);O':K$>N"`T N`*?MxK3tN2KU(Eg?ER8XY9Fu97k94Y+7FR<$;I(TK/?p2IZ.GP*PU6QVLZT6lyA_zVZg7EnNNV4[f/\N-cf:jalem`beo{VuVYp]KtJm]6R>KmC`](Fc-Of9Cc,EN)8_@BI/P]/t<K]nHbQfXoZjcnLoUihw@sLffSt_J]AJ_5di0~CdŢ]{WV_EpS+gX&HD;]=Gn7SQ1CDr?w@ly6lv>QVnDkEQOjt4hh=Li(Qh3rRrfkB^4gY5wnϡי͑؁__hJB|CvpBjg7td;G\SiGd¬鬒突|Gzd@ke:TE/P?q[l~^t\_f{lnTzZj]Zbmuyb^BPy4[W7]dEwXi@gY3@{KYOb=[l5h}Cp{IR\Gkb^}zrftw[Th8SXAayaqg}baXgFOd5sQLgFpa=j`@mbAokDszKLzGhHfzNitGfh=hhQzb]qyPoTztJzX~Hwz=i~Vj_srFgjElPQkvGZxBTm9Jf;UqErJbN{RR[rhft\u{DmkA_b?ceC^dF^nMcyNv|Sp`~b{\`bq~ƈuyƌoG]}=Yp=Tr>WuG`vC_yAXvATm@Pm;RoE`OguBL^58ZC>F9QF/YN/SK0_J0HX;BTI>R<>X8^N1\7EU7l`KACjE,Mh;NjOGV?8A515,Z:%IN"GL8M4-BD'HM.``'R[*Id1K_2>N8"[N#]|3ll:EgD=T;KK.VV*iw^c:`MYq\rn@XXKtME`+IL,zd4wR`}dq^MmOaoNehBop7_s^^bft?e{;s@JQ`k8vz@No?ؘHkIx~1NsTDT7OW-HK(2>#aII]-UE1k[,oEdaNis@tifenhxo?lbjؠyl͡mwq`NzJ4J/G@'aO8mH}VnmV@z;k`dk8rd>tqMcU}R_d^lp?~PXyOKoyFep8|qAP`oH`nCkoJkYPuKMg9SwogYqWsZdaK\vJwL}Q{HuAzNlVyxDfj>r^E]gXrPgqVzU\Xv̋͒礶͑xspT{`wzJxH{DckUr;V`0ZT+[m5zL}GS]B_jCXCjYqR{mDZo:_d:qy@GT/XHw̐sm^XzNmj=|Aj=dkBwW`~k_\Xac5>O5Nd>j_qkЗΌ|fSYsZfRuwQfzzszim~^bgdeL=i.78-b_CS_Ci=kFZ@RoCWvPPGZKe]mԠέŜ|nP{_~j‚᡺{bQqfqy[qQ\FPa5Va9Yg;fpNw~YnJiGrNyZ_Row=Yc@bjRnbHt@s>hFxUjO`}GtwO}KzMHoSq]wgal[bWdl{nsjWcawҕxUrtEajF\nKanIfxJbsHesJfnLwwRp|GVn@P\9UV;rnCzFeoH_lJl^Ktre}pIbz5Jo7Bi2He0E[+I[4Ke1Ic/Ij2Fe8KqFUjGYQ-HZBO?>KZ8?Q7bL4lR/YO6i\:^kBPcEMdAlSFbgIQbDYNQZ;FY54T=%L;%%"J(HN3>+8',I E7>I&TK'k`%eY.P[D;YGE>:DH+dL+Ys8[mE?a7GU1[W)Xs:NGQ`/Vb5gR.PRTlWX\+ku;_|MC]LFC6HC&CX*OM.d3PgrPc^CV;JX8E`.=^1@R,^Q1QS.CH4nM/k2őO{qtpqW\vo~bTNWgdP]x[oh:exPNxN[s/fpMVuHQvPSzFA~ZUVNvXHgHk^>buIYhLct:GL=c6WQ#O3F]+_a9Ub7Uq>Qi>Ld=ps5AbhNsSUW6Qb=Uh0du2[{@Tu@Y9^0wEMtnrpK\BMfeP%p[/kfAfN1vG.fp?Pohyh[Hj9tq{Vih\mPnerZqWJ{[h7&wEFaE5OR1TQ7]gDZn}TcđwwuYl@ReehpD;P2?>i6Of[}KIiBss0ug>nQjՔӂݡvf[Uxw0z~@\{ÓxXnyF}nMwsWtRiheh~FS_iNWzW|Pۉeզ~鼃ȎڛōhpswUqrFwSǪ⏣Ԃoa^=eG}gJdNkeE`uB\|RxUrW|Qhl^dxVLozEoqAsD\srM܅Jh,{}:e~Fx\iDLrcoɈsSwlFbi?Ya8bzCrk>`qip~т]vDkDcDzb@|IafuJsF}[~Y\gms{u€Ջ܎mZE][|_O~KwqF|WEiJhs~][H[s?{\x{d]EsVs`lth|fob[TmY`q윳Шџy[~]BdWN֞뱽مTuYnisZ}WZGQpDUhQvU~V{[{Ll]s\yCgd=_eIfse_4_z1\|5fWodE\p:^d4][2`^6gnQmrigqqÐ֚֐Άώ׌^dpF^bIksNarIaxF_h>Ub<=I(7<9LY@[lAGh54T--G&7A)EF-SJ1DO+@U3Ka<`W<_P,GU/Ti:Zq-Jl';\ 5S':[(?\1Ac5KqERxO_RGT)OJ3uH47]??>>9A3/A/=@)UD&Eb'Sa=XfB`w?>oJX_CqTEaG=Y9)W;(5(42%:0%&2.;I=I%SI']]+_J1^I;G]FDOALM.dV,K=BaAFL5S[(]c/Uw?JwDPf.Ki7aK1DzOJfNl]'qy;QmGHbKES;BT,KO6ai3FVOoBMWOH'Km8R_ED^9_F5\I5KV?Sf?Sd>]`Fdj>yfAkAQ{SBOIEN+RZ)`_.ov,r4|DMbKqIHh4QX(Et6@w@AT=:Z1/E-/;-.0)::9\CVp3\kJgFG?;iR:]8fR,hb3Wn@D|CFg;[T3^vCLrPVrC>R7XB*cc-FRMU9Q[6Y_=q=qnid[tQNiG[f:vcO2qU7nIvvETvzG^U7UD'Z@)_F.xU@vKsVgísjcsRX=P<_Fe|AB^7UN]f\]}_dP]QjKcqm~XboSqMuQo[u[nKCP(>-&b94t[Mr朴یia]n[reL[Z6OQA{h[gg3LO9qr]t}uXs\}fwdp\l\yZzsSxNl|FoqG|vO{eMslG`g@_od|˦ku3Z9\Lxtؕz}MtlBje:YX0QY6ZfXvp|hnZn]tgn~ĔѰ讱܏wruLVbG]i@^oBXrBHjKXqQTp=9J3FUCVuJ\rJVq?J_,0F$8B+NT3P]BOj8K\.a.F_,>b*:W$7V*VoGZvECW0iN4GM3?H:>;60C,J@.BS,\I/^W/TqBegA^Q=_i>c\AIvDQTJJc>?gD(XD&)*4)<9,J,:A*RO)^c'Yb:W^GhDeT:vQ5Ph:;mBER;[Y'Ie+N\:Oi9Zh+Dh1aK1Pp8T_LZb'Ci;MD4QL3Wm>GrCbkK?~PUO^HYRXJ1I\3AN;CK0P90ST.rjCPnEXKF\T6Wn>Qj:gp>xEnK;|2?>K=Uj#Py-[n3dz;jtFT.MX.HT*_@$jo;}KybWfiFXNO>mW(JE@hLGW5OW=e6cbnqOjyCewO{{GFro`rNq|<_COsWPl;eh2[M_sPmiHZ_Clj8LqJ`1Ui1Zl-Wm:_}CM[@zMT`Ixk@hNc{R[vOyKLaBkNKh9En0Cg"[_Qr8?gJFO$}J.USEThUi<=z4:b/KJ(SN*uR,Qo:Lf1]h0S^9|j7zyH{[x`guo`oEfm2vTmQWv\\_mRWucc9`Y:_gGhlIyVy\ơjlRg^:geDXgCrTDmdKq{Q]d_wId^A{|]i]\PbvCfK`AfvIqOqQdPgMe|;Uy;Ui;xy^sQ{?*6"'6)3I9Z]Vrϋ`oo9G^-6P&@F2_ZK{UAe$:E&IL8tbWavVnPipppv\tNi~JkwM}VcUxY|ztlZ{=`{L|~֞ՏXuCrj:e8Y]>VmUojŁ֔߁ofo\kfvdxmƓĂfznƁQcmKis?QhFXlPfjwo{JO^3BTHYsZ[{Q[xFOi77X,)G,;C9MZAUl?Sa/]P"HJ"1G'E^5P~EK~CCo5E_,:H)DM-GQ%8G)?U7Ol9HP.bN4GQ1EE3IB0=A4UF.\Q1oM8Qc9PSAMe@]aBF_=bK?dX;mPhVBt_)RP>05[3!ST,Y[>NhBaT=oM;cW9VREMI>bO7R[6pS9h]?s_BQ_AWU?M[8CL9L`4Ae,FS,J`.UY'>[-TQ1A_'F]8If0Hd1Ga4[[9]>clMuCgK]sVccjk@cIETi0?W ef0KTBh?gI,R^5|o.IQDuOSa2H>Hq:BrBGnmIIzLIL+YR)|>u܂pvaFt|@iYK@gRWd@cBkU|јԽFaDOh*Wa-wo6FāȊbxQmzI|rQuLlqLnIMZj]~=LfrdftTziEWK0J6K<(UG'}[.s5ήYʉ~N_6pyhqAwiC}·qo|КtzsxqpsjrmuSmoCYN,iS>_qnQl5hu5uC}xx`se=ka7ll9ioAlGd|צdzĊr£yjblU}T}KP`pBMqSDnǶoMcTu}qbFIzJzE׭{bZ_kUsN{Vչ֘lvUmXx_Me^otUijƁq]vAvg}~}҅l_P~S|QxXe~^Z`{Ȟϐsh~]Ny[B|gIv~R[]^miJ`cwX^~WXUsKeoFewGUo4;B$PP+X]5foB|UPWs.G[)>R(XK?jfI`b;SW8]ZOzw‡ٖݭkiHUy;Aa)1>!14-jeXjSKBGBIHRA`j:n}@nGtMdrzxzYpTpUvWUU`jaxUyQnDcqPhrUdGaQoGrX:haKyu\pbxgة╞sxkxhwT_yRjjrdvUofvlP^vO`Vdf_bfmtz_Xs^BLc>?T+5M#9L%W*K\=FTAAQ3TL3HJ87;@C9:IH1|Q4wi>irJV`UT`RL]I^TBoP:>hG*~N(vcF[RMj\XIDfN8bND]TFiPAZSB\TC^O7rd?dHROQfOOc8Cf:JVm+@E1?J(4O36N2JJ&c-9dބitGXRQhJaT6fU3IR2hV6rOgZAM>VBPY3J]0`U6W\7pd?ro7voGci]OcMsUpJOU`gM{mEvX`Vf{XT[CVu;lf7MlHMSF[j2UjzN5c>AL(UQ+eq6LHA\5JM(dJ"SFaiFY9Gm9[w/fM]\[HjTk|Gug<t@YрdsTzLKj.qz=P|pzlj]dcAWHwaEplx_V|@dW8kla|k`gEm?zHᕤoqrPjQ\\Z\:JU9oI4b|xN]~_{gqUgRsAd;Sp]mN>\/H7%X`-l;|zу{|wؾtٮRn]Mz?cy9dWUbG=nr`~]j}_~fnZc{:Cdyˈok\YGJNL{Y]Nc\tXcLf~I]k:Rc1SoBbM{J~Eض_gVf2zf2Œ]NuAzBU`~rf˘ִ笑xdJKf4HZ+{=pj\Ph8i~>bNnh8lL1I/oU6q|KR[U[ܐpdqLgb:kDglsUrYgPnPbLJ\nXܣ\Q{?Wv^sJ]DM~=~U~\a]KnIUmմtof^sbZx8s^3cùxs\YclS|Y>jK:na̎ӌΆsasv|hSiEi]xhn{NqL{IKxxO{xNHzwWm{|nd\zM|RP{S{Vk~EsyI\u=XrBO{8Kr-Zv.VzAkRpKd5Xu0Wg6Ib;DeKQuYXV[pJiaVtq|ӓ᝿ӎt{L^v:GU)/5/.'QSJq|dblca?Wq5d|]`CkDzqA^w\}jfxdzԜ噟qcfLd{@Tn7Q_=emLxLmGd~Xpf|Uq|Im\yxjq^ltjzKI[:U[SkchO]JWsEbvG\k<@U;AJ!KC(|Q'cUSdKX1:i??O9FH'FA*RP2fi/_n=b6jVolTwTdtSpxJvDVmOhG;mzIvSld>WeEWX=Yj;`f5NQ7Rd7Pk8JW2PV3bV3c_8jaAQ.\J'Z4ErJPp7Wn>ln5m~DG}^\\*FGEnO:]%F="f=o+hasOm\p|GapXTb_7lI_HjJ3ЍPHdVK2q>z=fha{ixQiAfuD~>aÇtez`SuaqQALPzS9k1lU}N_^ruBTP<#_f5UoE|9TcheljhnuNȡmmZu^kIg`blmHxE{JsGWasT\?l7mÃ`׶阀hgrKqn9~GuciMpR_xQlVj/}[-TUVlh[M[t8y8|VTw.lp/fσmp\muKirye3]1uU`hMMaNwԏmd_v>kEwQ^yEgc:n}Ent@V~[Ua{@wY8}F6mτ`\dfVM^{MFPgbvN_aXq:eS1ƿyfiYRSrQlrGOgLMYrЄZlDnb_cH_z=q|0Ad[vNa~a^ltn׉XZSmKvnIpHba{sGoHiO_ofhҟskj`hvlNzM`hsspx]O}QUqQk{Mv~S`Ob[z`rE_v>kY~iZuCXh>[]=[VGuxfthc{YghTx_ToYzqrnsxQRP,=A.;A+J@/JI8w{VP^{QZGdJ`~FNmAXnBcpBcm:i{EqZom\Jko3SM0XG;qsDx_wh~UtOwQlKjs;hl9_n>nW{]Wwc͕ד{Vg?Ol:H`88K%9>.\]?^GZPiRhETd7Ub:dnJu|[^bh]jmqJWp99[NYk]k{O\g:Ti5DW5Fb6Nh4fr5;PA[?@W,Q\.>U28=(=;?I!6\41T*6C*9 h*XޒjRqJ=TD];;U1Q/W]:g^XK.M`1=Z;OF)WM,`h>Eq?LM:yL-Zv>WYQjV;gDePc~L;vRU=Aev3P~I]b0qyCWeEZ^?Ef3ObJX2ES+JG1IO0ST;wk=]pJZMZwF_s9;hCQS1Hh/Q]3jf*vJ`^R}\Nx]\LCf/HP.YU-F~Mw{BGQ]P;IU'HO(dp0J}PKV+=i7@dBX[7Gw4Ef:e,T{LiLuG?uEl`;|?hzrlfr]kkivbDguDn\FuYM_VgEPdMz\k>Awr~]CS:c;FK/oW-zF[ˡruiuWa|EO]3Kj/9F,>@":K'aU&Gwъ~yT^joo>tj>gKDwFzLov홐l}h`nPVҗgdIYyKyxEYQܡ{znKqu=aCn]3b@ZޑpװŠn_qPqCi>RՂJ=lDEQ'YT&fp+Y{7Y+|L`Iv\sGOS|7qj.X{xStzGfiCrHxdy[bkfj[\يjNgCRK!tt4yvt|HQ[QW~KzReZ]S2gG/}LgI;^HU81V+EL(B"{/BئRksnf9]H_NKUsKf`K^3Zl8hF[_JgL>L4IJ$Vf*^[7xDz`c~S]uJRyBBO&9M+EO"IV-O`;]y:xyJE}O>4BUfGo_5I);H);P/KN#z<vb?^\Gt?Wl4nCi~y|T[REaTeyPwcNzoYpomf{J`b8erFoLtŒ٧ۑjφČRj@Y_7tPsTbIZG{LpArwnDxIu>kr6sm:`s@gImv?ci:VZ2TU.[[A{NxJ`y=^_6]Y?rcVtScnA[S1OQ.]`?[iEZdBM_G[k@Tc8^TR>WfUY^q=ZtA^{=Rj2A\7IxJIWZiztukmcshzRdpM[u]siobpWog{ijLNkDOc8Pa3Ga0<]-8N*1H.;U-DS2PT7[[=RW8?]>Lc6R_7Wb9bn5!-+(&?(!U6$Q=+iF2mp;XVBlMa=E|:8J5NLJ\SHPL]Q8nN5cBNy]kiMq5[QPK;DK/LD0LW4gW5cQ7^eAlgG[eDEeJhC9L/Sl?EII&G12$A'Y=!\V*k]1bf4alLjKzB]uOUTEGL?XF1ZP*zg5nh57bQDH?BG%BQ-CV2PO*Ac/BKB>P6FY6DW8E_\YP?{l/R?F`JTT,9N)MA2a&;\BMK`T.Rg=?y[3j?)K;?6+UN(UxAEr70X1@@&>q9\Fm\NYByO.=NyQmWAe>DQ;BT1[g/hu;L\:uJ3Va<5MHC=*NR%U= _]8|u2YSE`W[>hw;KTAZ6dS3U|:]}LqPfDVpQG[9L[/ZP'}S(tzVH[agRMPJI?q>2Q,I<"Xj1jAjSEkT6J.jR&>bo;[er:]k:]r/?\Tclu{fdeTLSQI*s^;f;iHRUXjĀbYcXZbPv{M|wM~^ӣwZgrD\V/xmA_s<|RuntAmBuhUsRyU_Rkz@cq/la9rOUMiElv8e~GLt??](Q](Sj'jl7`}Ash:SW̧Ŏu{lll﯏ݡnY{IKxwFq\@lcDyh=dJǐfnѠeIlk?y{STT}ZsTwGcp?Wb?q\FgV{bz]rtMne?jc6ztDVRKtCiHs}P~zQuWbz~ĕע΃tPei@]X7KQ5BO2?L-@@,GB2hWL|gk`_ZfzӾfhg{Yg`uZ`pCjGxCw{Drq9_n1ZnPyGlc|ZFn>:D8DP9IN6BL@\kdqXp9\X:arCZCMvJltW^sb_xUi{N]oCSdDQbNYw[Oz`X}V[JeQs}O~~I]mEL[6HS0Dc4Jf>Jf>U_7Sc1CZ4J^8VdCijS~T|DYj4PU3NZ2C:$=A3A73L;.MB2`=5UG6iT@VfK2mH?QCJ7;R13?54:941D,>7"V3(<*fCXbFvvMHM?e%UZ,He0@E9=F/fHWU>Uj<:\?NL6oY#Rw9TrNVeGYCt6Dn:5V,`V-m2\^DE\C_t?Kqt^TORLvLUV=p{9h_Cld=b]ETO64P<<<(?T$6Q)HH(nj*mCd{TYUcPNHQc4d{GrIbaUZVq>Qw?>ZbV4u]Cow@ItTzOcwapie]d{|mqy礙sZRc6\X,CA&P=״qewD_jj\=}=a]/Vj8OK.mJkAJb3bH)WZ5jLSq?=^0De/Cb*Rk5pUgSRvvE\?jg`@^Ukd}eƈpǗp_YrP|rM|jQXxYclg[}K|c:vmH|YYa{QWb>SI7lUGdOgUgk}eyTwuLmpKwQ{LfDfAgCjmIW~Y`tȅɣݙtqYj|LsyI\sE_kMQtEMV<]pPueb{KuMPQgeR|\b}W}gY~f|[^~Y]UwGl{FZ|Dg}VxZ{MqsB[p;V`?ibC1T^Ko_lZnM[{<^`@^bCSkBXpQSgJZn>caG^wMdOcbΊpNotPijPvjgnBQT+7;$DN.Zj8[w?Ql5F\0H[0HY3Mc;Me=OlBNfN]rbjrpzqu\qG_nG^mTdyW_uJOh:MaEUgDUU=NY=R_6Le8GdARhNXuO_cHg[?hU0CT.BZ>HL:T@=iL7dW=MQDA;;g42LA7S=0pP6JoH2gH3>H215>)'9(83">6"M3#C,\|H?}mZW\?j@TYEVV4Um?U`LQYB\\B^=<[@TL*\N'_S6bT4PT8YJ:uQ5b\?I`CFX?eP+eg0i?c}CNe[NK6?d34V1I9,JZ&;@2O8)OE$>T7=I::F/EP.RM-Lj2MXE&T9+4+H4qP"~o/gVVkauS}IdMVvU>Q-X< J%cB@o[>G9F`=ncEAvECNACc1RQji=tL]JpJo~QxAoSgq_elݐvbPTccuY?zC=E:W?+Of7XX=<^=BN;PI5KO0OX4fi0^CTyJzeGjVXorIdVgW_YQ|TZx@^DlSWfkctm^jDjPAPa݄~JIfRZ`8ExbȊPhIb3jS:Q17Q4?Q$~V"nGFZ;dMZi6JwYOYMrBHT5Q`0Jn:J\2Jm8SZ9}h;YmJ:)CH1Gd?S~WWfr{zr_zVvTk|bxjmShUnyX{{L{xJ{|FquDXnWnykTdv=Ga2E[:U`MO8;Z4-XB2V?8AC3M72D>'B@2I43U>*lE4Xm=9zM-^O)=J%435))E.H?&_I'jFqxR`jLj\@cKQU3UT*DX>MF9K^4SJ2HQ54T7d=-c])`gB\^9yWBN17'?:$4R'CB%ZH ZO,\`4_vBSEINH^>Tj:BDE`DZb+Oq5bVDunz@XdcqPdVx𼡶}j_KWLJc>_b)h|=Oڌ읕̅voؕf`dHv^dnɁגpZ[uwc`s|oqoEkU=rRvmzyifjcOqbew±{ќ`zT؊AkrĀcl6tW-d䮍nxTѢRpe}PlOUr¤yy\pul_fÈh^F̡zwĮsӇqx~LkSu…ψy_nqNTV+ix;]weTDbͶnЏֲєrG@Q®abk{jwJ_˕ÈsxX|{鏥ԏlsnbb[lwUYrXVr9bx/]j6yV8qLgks[v~HPiCVi0xZxRQzamO]iqR{z<{Ri|E~]=}VwtQjX8nsCy|St\OXP_CiaVpR{Z䴋׾sԜdx\dVxɈu[hU}^rx\wnevnssd|VYzwOy{T|drSZ_?^oFpnJbu`gwpyJz;^{7quEpK}JHvw@su;G!?H#>R#@R%Cf+Z`1ho9eOr^pLoܕ㌮~懶쉬їߪ햙qdqo\dz}[ky=HK'C?0kkQw^yhwvnefqƅЌ}`e{AelS쟡}ckKcg;VV;`[>e`?g`CbiQtpLloCgaNuaq[zqVv[exϳ¸{^|[y`c[sPWCS}AZ>eGdBCk/6Z8InSxstȘǁdt~Poq=Ve;YlLxU{Let=LZBOWUstkt|QlnM_mFKdG\oN>14\:0[P4UR=[R7RI9R>5JD7N?3H@2^G2\T4nQ6J>nYnjMTNu`NcVaTFjM3_n>GlT:UK3F;[C3fa&]QKZTLe?\cAPkDm`Kz`6{S~jQbXV^A:p[;caRc`OacKViKAbBYE-^L%0R@=;4WO,Jw?HiOSj@rVBkFYuD\sPT8Hp;5T>GJ)JI"1^>0E7=;*2I);=*BFJD&bT*\W.jb1U>?TR`QXh?GS\jLNDy`@Ww>POHuS5mL8{`PG8M^-cl0ZDHvUK_Fe@Di;Ea9Ha2wd8GkD}HM7Xl9oyAtLjMdYsRV}LfySGqF$CT$?c2XS,G/QoE_yFoRVg]kJ%lf9UOFxR>d2CK#K[%>]4=Q+[V%ru.PqN\W1ec7cj=Xh<|Gywƒߢ䝞hdkGowZgoHce@Gr7V[$\`4a7pMh~hyoplspoy}ҋ|肳⇔c`㧈fKiJVulxqUpuDM?b7'Y<ܚny@oigUXkZf?j@ReoқuPqwMnLdYd]yrӑIu@Nn^eS|=Nq_ejJW@w^rswyVScA3<P_,f?h=mMy|AexDɢpޚІ}Rl=mb8[t6_i;Vo:il@jjCYh0EN&>W'>a,>\,@]*7d!6ULf*yK[Sp`Ăה嚠{h}[oߥ߲墯ӍȕɉciĀۄZpI\7;d,>P8bwRood{`rOepEibHaxuplS\~Iwϐ}pzaxNnsFqrEtoEppLi}SnT{wCipLp^p`Q{_\|dwgyZrUoKmL|SWqSjQkQfSmIb@Ss>ZlKp~Xel{sy~xmn}_yY{xGpsEXk:Pl?]pF`oEdkKcoN`mNr[xVrCeb6Q^:KSGL]SVF7}S5eW;qK>PJ=CG8I81Z@0BD88><*-1V,'OC!V4.B,qBk}qz~RqZKVIeY@FhHFKH?V41[7aD;k6KQ}^YxCqRowSzcK_ESl}n|tjTJZ>rJyp[[eSJYS9V@HP'U]-Pe>tYhGjp}X]џ\LSZxhi>EL"VM C]-ETFAc>S.:S*DK(eD&hx4Q\a^\_PesSPTNiIAU;E;'O"d}GRsPDc8F[=nA2P=BL(qa=lZI_9X1?K&=[5`F$OvAWэp͠]fuiڞ`m~VVrԀy`Rjsm_b{vDa@OjFC^>:L,W=itw}̃aP\BY:Q{.V-Z/Vwia`p~ms}ykڝĽrsjAmI2lSӟrUuOQd{{s~|fsIsWie\yVb^ilc[jWyU\zPhҴoNdq3]f5^q<]t;^q6]x4cv6Zi7BO(WL7V`;Ei4Bl/C[,FY+Kj,?n)Cb!Rs't7BpF\xɟمĒۣ魴䭲駱۝Аτcza}l}_yWXBW?W~Xtzt|i]RiOp^jrԋ~gjv‰ǂ}ifSqCdKf{Ps{\Zh~GdxSqvawXoHqGqWbTrUtLjr@]^7XhLp`|^ohtfqYgN[v6GJ-DD@YmTb}orx~^xNxf?q\=UG3FJ3IX8Jb7Nc?PcFZnCSoNXsPRg=XZ;KY?M^;K[6DW>JZOUqgc]:k]BhR?[N;_=3UF5_A6bFzeAxv?pPtdIjJvGhUuMWvqicXXnL@T=iB`iI[eLBSMGLhS<]7yC^x|u]JfhKtWUYHeVBP1JQ+]c&dr4gLdY[UPSL|ILdHGc@M_4>m>GX=qW4_CZeWqXkwVYMBW:kN\X6p}88ZHF9.BD#CT%dZ(\~5MhIa]e3GP-Rj._x6g>JA_d?x3}M^YC{KFpGCg;hn7Jzs{ZTG?i6Pl4ZGJ]RgCdc0U{8RODLCvHS}D]Lq~LSRe|PHMLXDVZ1c2p>_4_1[+U~'Q*^r)Rt?IuA:U=QM*w>yW:b,A.NM%8b=CQ+Sa-&c:rgsMʍ_ءamXYS0zL5kۖlkshRX]ipVcIoPz[UyŔcXeyw~]{ANsƅpZnyW`XVkb}NZ£Ӽw]TO4DD*YH-Oe9\T6gg|Wo透StTmTn4X`.i_.\Y1{a9jmydXqa~[ds`OWfִzk[^υoPkOXFtĄ{_U¹䤢ؔӊjmY_ZBaNŒvԝԋtld|eXUlPrU\f^~Fvp9rx>w_lm|ᔴϐͤӹ}lX6DG(Yi2`ٍ㙨rYT]}pEpY:Ea¶ٿҖיؔxgYaa_msinb^u\l{RyXÏg\Qz?[k?bsM\3DU3pR=dSUVF]]^fIhoLlsOoLuikZ^\fj\]TYEQ1RE>:'@U*A\&YY#F{7EbC=T,^H+ul$jUhopY|QțVrEIR:[?VN/Ur8lEvC?TZ?VFIZ-cd'wr2rTmgzQ{ammfPgJURB]R?M'V].[v4[*U}+S,R'Sy*Wx'Sn*ExK=Z9kY4hHA3a>E2Lf'C\%>e,LW:A]0l_2cuiuxt~jwYfL4Lq;VeDehBnzD}LLZM>Q-hN'{Dt}WOjDEgJeZ8he>pmKj@vwo܅ڔs[WHi@g;njoppO[qOwdLsHhVn~hyGpFU^=UnX^XvKͥrē|UzPkɆȊ¨ipaqֈm˔z{y~{qtef]{Qj|\muFz~caQu}|kכ歪kK_JM_u@U\;RG+SwfvDbM'{L_sLwh0zSp~?^y5]CFtXUz0od1Ɵe[kAxw6T򥍣[]ƿɉ^礲ߦ|zcup˜쪙מ쎮qwhqΏɂӒҁ҅r\\{YtBo[6bf:`f:qh=wvCdxF_n=iBIv~FtKvFGQ%\n1l;F~HzOk\5VM.VlAaMmK}BtN_c>e^7zK»xhkKau~Мʊvbo|̕z^jUpMiMQyyCoI}KuMvwJw|MxxFyHwx?mj8lsJxNVAWl3Om3JO,47#54'HH2HS/NS0Vi?]CI5U6c1f0L|0D[KyӮ䥵Ҋuvtlb{ccsPnHvVgB^~TdrȁfzQoRj=BH4Xe^}f}pȉza}XrؐۈmzWqTl~RrKqOw_~[~U}[nZi`sc|\rG\t4Lg/Ue;hxKvFlE_7LuIg{όߐ֑qsu@7E/MGJvxs}sZ~W=fS8JL:DO7TM8k\8Yh7Td:[a?V\8K`DUgU^ybdvZguRZb?@O-8O8?VDKm`h|CapY>RN_53hd1h{Lc\^yWkYKXIH\JUI4N1i_2,ED/$0* O$2!e>8dG`ghSHa]=u\H~qCWrOBb[VRBa_8duEnrQkloigernprdUO]HnP{ac\{ZIlKJmGOYN\W*CI5P77>*a7e~u`jF|dSNk>TF]}=bGK\Oe=eP+c[6Ub>DpTRW<G+X/_RFtLdYKtGPUF\c@r_=vV}]]gtriMviAyW=J.LN(?l-GY)^i(?rALK48]/=A"pW"~?roch}I;^geKd[j8dw55|U2=3b@"W6[m;NwJOpQb`6Vt?P\?W[6\s7e}/\y?Md9HM&K\/Ug6moCtLyeUnYwRuQ\zM_eCzs4VK]K?{@LV&I_)rN,`4gsQpuKeKjLYKUoC;[8BU(MZ)Um/X*\+Q1U+Y(Z)X|/Ml@[o<[{BFg;td/H\?bB8]&@T(KT+Sn>IbFsCコzjl8jJMW+`l/yk:y{E{UtzI`cHFg;QW6jZ5eTowERuPWc7ZR?bZ7?\qaOfʁvnY_s^Bc^={MzbvIy]@yOh|XNfb\IbL3hA+{F8]EvRdؿ{g}֞Ļدe]{}SQziPpbup|Vudqmh`\pLWsAswCo@o5q`tfu0uo0|CxwqXwPuR㑇꥚vrbPaJ@H(V1p]r?1D7H%Q^32H'I:zc.s]@]a:k^)u~=Re~YJhJwpq_`t`ue۰[錃g΋͉`yTƋ֦_]jtؘי߉}̆⑟|~rqmm~cc_O\wIfsQY]muAwQ4cVj=xN{WhoFgUBRN.yt?jF{\tbn[qOtIS^~[T_zȶv^oWrUqRxIkDaa7tpAuEwR`VuMm?ol>uRZPlIg|@R:Bl/2W+.L'.J%.A-NVH~`lCX|4^3Mn0^{EZ[whysܕuSxCcr7ec7nlseTl`{[epLOTI{aRjvOSqGQi:cd6S_8]a:[m7I^@P^LcgVXbFJEAQE>SWFQvTK\TcJjH=]B;I5g@'nf3uKL]LgVmhG=QD]VdD3=;3?11H+ #'/0,K3'v?'q[9]VHxeJUTVR*H1OwxeZnayKvIZpIsFJUaXpA^p@\mAXBavBPaSQBrH2_9|dgڭmpbodlzXqsTYicmn|lM~O4bF;T4KX$=l,Y])Jz3DbDBL%6[+GX)]i4zxDhXrgPdwNKh5S(AW+tZ*TBKcPR^-Xo/Wg5Px@TxHCj?KT:Rg5^~=_/Z0W6X.Z/S.Tv9Mn5B\0AA#n^4cUJPFa(>q{qOx_IxF{^_ŃۚӄբsZiiXUi|u݆욐~‚ØШiNL``yS?GTqAakMY~W[_Z`eexn`\k{EaR5ZQ3ZR4jlD~SuHRuJYs=qFf`tR_wKjWv^\t˒ƃwh}vXxvUasIosCxyDnIqV`EgH|HwEYR{wLywNntO]uI\IlUdbYpXqduȀ{hm^oWOZs@e}Miu䚺wl{NI30Q$]jITwLlw\^rLhBcuAc}TgXo\^MaZ1RL/TX.NT.?U:RhCSkNX7ITBJ^TfxOVpFHk:HhK[^{feKgF<[GJM9TX3XgE`bEsa=tu;~JQ{Xa]P=Z:7?7I7,$R- :7X#$;?.K989.e1!tW2DP?n`JFQiK;nf7apSU_WbPM~{bvp^w\_S_YGeLNQG`lJUHRxNAO[6QZ6ek8Vz;Dt?J\1X]5dcmEzQwm]wWfm[kqvP:j>?JCkM"gDwB}E^hEhEU9AY.`G&J=HlC[{:ZGTqGeU;FQOYHTu8YCg6b3U4X,Q,Ux1T8@z=0h?4:+k6lE_Zdr@tGx}lPtVPf4qHL]avChTa~HSz:hs7f6op;KW;Q_;bpDVh;muHcOFsG>b>8[1J_.e[3>V6SM$Zu0{{DrWgzPLO}R_k;`i;td1fb=~NcjeyAzs6|Rlg|~dQxteFpnJ\쭑mƉK[|UirEXL.reCZXq_>o_CthG^c|mcѧvޞyjjo?ae7c9qqtyW~Zmjg}QMuEsU-SncZSBe~8{Kt\rMks_rPjg_uT~Ճotnf~D_W-j}Ce[MIM3FI;GL5`w:ZC]~FkWĊkq}irV{zMtBBhfPd]aMMqef[gCTiNXg\}fƲυqIqU~mϗ笇fwPLzG_m/ig;cՇąʋӑґߛևPnvHw|Pkӂچ_NTvI~Rloqmfh~w݋Y^cbGXjA^g>drBfh:s]6fU9`f8|AmO4^E~X}U^_s`2xX.56?:GKY_CaW*`b;^Cg:TUixdpWgNlsL2]WPA5vW,~xGXaOjHa]Ih7O\7NdTo3HzEjY5[w+`A:Tgt@z<[ADZ2i[&c?nwAsAX`rSx`\iQQ`t8f:f8Y=],S,Ux.Pr2gb,ZC:X_TFp?IU]S9Iorihw{l|hmodS`ZyN^|IZ}D]rCPD?^CAK1u]*Kc]Ych`cfdvlZTPd?7W9=D!H7 X_&Fv]vhdpObeA[MsWRd8cb,u8SۈhvRtBdh1l8r]l?Q`bImНkeϜoٟak4azJqpI`dD_\:e:|KsF~V6U?lɩkbbiGYh@Tp@P^:yW3th7xk8fA~Lrw@Vk9yjCon}n^jL~4rn(jy2uA}?QV|mJ|a8a9OrRZWlB>Q'aX)~{9rNXsBioFjNsR{MjXasRX]:dF3g\7o^5oH.T>|SizHv^:kB~NvYKdn|lmts[~G_5In:HyhDh~HoUdRk{M^zAKc3@](4S ?] Gm2Zx>Y};[wJa[a\gPaY{ߊՃnx}ᦫ훦~}ezh}zkj|zmzPks@`c;aoC}LyDnjAau6Qk.GY/FS3GU1@E1AWEHfR>B'FJ,ZK-k]?W_5ShMPA-jJ3d`E\B~PychbSqljabwHqp>BH*>B"/%C$BINT5gY3_R.PeEmYHX6uJZs~rLNJ?[IAyY2pg@kIQV]GtS9U;;C)?P'9j9b],z6xzZacP\i>Z{Gpm9r~FdJ_dJ~l4K~|ljX;yAK9C^0X`-ql0qJEYIoNGlB>X2KE&FW,SwEHrCEa;_N%My2jl=d;OPGYD^e3~xcGUEat>DdkˎLJFt;p^*ZeuIQvtǠr}~vzfYWqFlqNdGyQ7hfAWuױߙR{X`AmNc\bbciI]d@Tc9ag5Z[XiC[n8Z{;N[.DG1^Bm窰ƀRG?s1Hi+He'gv?y[j~EdxBmvPboHzG]Y}Eh{GRLWEeW_|KZ]8ln=Zri~X`GKOOeIlnIsM[^FgG_ZbRcLs^sWiTuQp@+9=&;E%TI0jT3hKTmyJhh7XT-QN,WT,]W7cbDwEtMiuHY`@_hDXcGiQ5)=-#>-#E8$1< #$M/0~oOcFJs;fj@H[/@B*3:#%3168u{wδWU{EfzB|Qf{l`Wj`ZtdnlbglXw[wTvJizIj~Jr}G_{?Ltb>iNlbn[nSdNSBHq3?_ =X!?k&Gq6Nc3Ob?h\xp~r_OWrJeՂym^xfǗ۴v`}Z|ezskk~ezT}n:^V1PS5Q^AetEooBe7^`.CO):C%7@%5:4>SO\yQ4@/FB-HG%TQ6g]IulPblS]SKFREGJHSF@UD@`NEWLBZSLSiE8^R5`QMbQEo[CEgEZEDNe;NaHoJ+]CoO]UsQMx\IuJolOPjXPZ?Be2>b@G3/+#DG!Cd;_^:td9o^@XrQyTJ|`8lC`JuKuO};R_VP8df@sm?fHJY4bH%K5=>'KY*IqFYc3@d@4C5I=Vg,OcBgY6yq-KIKcf:{6vuؤu@F[@Oe2[h6f_4sr8uJjVAj9dF9E"JZ,P_(Fb3;r>TP+Ux'fyA_?KpKVj7Lb?~b6aO:U?VM+oR)NmDSxJ^\0Hn5PW2qk5Y9~9Hr}cLgSy:Gm(fi8l7t\VWcZWhc~Br<\CV|5O~2Us2Q3CF[u@tBWvkjbiWXM\ESo4ge.Kax]jBdyHW[dVpAfYqVbjWpgYmlbZXaCWfDwTskStqS\cH\kAhQBV5=1E:%J\AOj,|Lrggc\|RUwBeLO{A@Y)^f.jGdtӎcaKYqJ^^mZ:z])}r7RsnRbkpxmUzy\Y^hM_G1NĬ͝˰Ь糼Ňq_i{ckNPkQNV1poARxeqUP~V:Q3gM7fqauL4JvoZ`|>L>Ar0Uu9wRb[UrIJy~UkweXtunq|AuE[lvqsjTlRY^_~5<[]yJwR|qT^|t_uPdij_c[yHK?LK3\?n؎uu[Jh催pqyh_}qb\QZOuGl@dA}GvŅTKZe?nxdyfzĀ|zy}ʓyrfJiz=}>}BWާlwvMfۓٚݎw^qc?YO7CD(JI.KJ/]Z8fb8iXy[Ne7PF,X[BXcAX_>bmEjuK{xHOWfPepB[Q:XX4Tn8d}JyqI|aBZ?z[Ew^;zbDrxUgQ^oFF[;;;'71#13vVbEkoCdzJLPL{E:b6DI>{rgesGl\pW\JeLahSgxAis;uIwύ̄jLuQwU`NTBjV{Ki>_v>b{C]BcGiNwVXqm|qh̐f[xBL]5CK;RQ>ea8]T9bb;Tw@^Sd^z]uRtzORg59Z!=V(Jq7a~M`x>KwCX~]t^TkXZXOrEjoPu{كa{KfoPwsl|r\sEcS8Xa;R]5VZ3MPZbEYdK`RJaWEpfI]pHa_IciLgfQ^_MP_EgWHUpD:pT'8R6'&U<Sa1tR4pf:dlR^nL`LGaTCa\GgWH_fHU]JFuKR`CjjCbTc`]cjKMbJ9^aHYSFTH=VS;qU;PHhfW]3wo@`kRfUEdXAi]H_E^m~VxXL`>lc9MFGMULcQ_jX[[e@T;rY9foSu^I]^AnTOg{}bsIUCe)Nc)jT&bwK_LWvR|EwTXlk\[9l7WSL{Her2qw2d;e}GkD\uIhz=Y~F{ONS6]6TM|W FpCPh>]JW_COg5XpCks9tJvT~e~WRrTraxsn]ePXR]J`e5z]imHz^HN-_q?XVJ`sjlqiUKs@ES*Om-VAy|@YttyNgd<}xE\X8J^45P0WYPg{RRa>pfEs}G^j5TK;\fdo`xdhjya|[n{hS`v;Pd?_VbfPm@fzAgTsezhudoj]gC@k.J\)Wk/[zb)Be-Mv@ZPhTtYzou~ejCWwUrDgr@UV5JSJu{SwGWj_E9u]hRC^a5soBuZ1G6zY8QBlIS~[vx[oqUa~IikCb{B~O\uӂwڒUv[}I\p0}S,f3}XbdbUTLOe>GS:MO8ibCaEK{[e]Lhi9Sh9StETGHZn?yZ[b36q>8I>JP%9[1?`/L\)nn2_@C{UdXCo8^³xcHr\X^3Op2:l?5S*@K&tQ(p7cjcYvf5^e8ea6_l1UrEsk4nJbmㅇ݇]ImFiBHV2AN)IL)LS,Bj/Gf'Zf;U[2^|0NlgyH~BQcrǁ=dMx8WEnNVzYwStIEk|BOw;Qz5~s-bZIULm0Gn2Jd/Sp1Pn2QFHsDYR)nCCG*P,1F5Q#DH\k8TTM_`AN>>J,L_.oX.g:d~q}mwoyvmezTatّt^qWrғ||iiuyyswS}ix}t``BkvKgm|LdMgAYg9Wa;UR4TzCe{9tDoU]~KdwO^zKlAzPpElzLl~7oGc>?V.SP+nuQvLb}FhT|Ⱦzq^tQa`<}U>ePkkjVu}Sl\b]`YUwH\^G|seuclPh}FQh;>KOaz{Z}fyX|FvJz`y]u^rXaLUzEXxBaNtjjicQ}BNy4Be3>j8VDV?gAlNgNX^{Vrn~d}]q\]HjxBeIGH^&IZ4Ea/CQ.dA%x|5n^bh]kq~SػCk_Ma5L`/DT7OZ,\R&T\1~_;hq:S^BpBG[-F`)bO*@o1pS5:΃zs~omgSm@{Y=h:Df2G\4Yh4]l1U~:XqA9YX|fb{LQlqxjjyaeSMuIL\:FY6QZ;@^7:N.[b(U8z=_[KsLQp1Iy8Jn;KvEih=rL7eKH4Rg1>o54S#;K$,Z BO't~7rmg}N^ID};Xee4Xi>I]9SU5\^@\bIRe>{bFy_s[qF[y8[7Z;`9n:XYhvEQX,VC"N=95/*G3eNWE"mT5tIu]\Vtۅ{vnĀꮝݚndkImyAAQYӕrf^MnŒrn_xvrZs\YMfk2deKoufbagwHWl4Kl.Yw2Hcv]tXythtY`wXe|JuzJ[kѬ\z铵derGidTmo|q~iuZsxJm{Jt_fVZK^[jY~TaȉYP~R~sLtd@ldDob?sZ}ΕuUp̼ysQUl@ndKcUnHg{UpŷЋ~cgq}x~aMgPp[}X|^n_xg~goZ^Lp`jeWyRr{>_r:Mk7Cw>UF\@d8RzCpHsBkCkFcGnQcrK[fRm\Y};Wk;Yo9Ukx:ke>_cAZx;U9LA5lI4_w8IR?WTOH@RKthCqaEyiH_b=hsGYq@;`FQF-Ef2LsDitN_UVdVoRFbE;\BB?.RR'Mh/La(`f%vo!Xj#\Y-qu8]NS]Le7iY*s)vCetX^yFnOTv^HIjQ[l+M;JxHLk64U9/J-+E1`D4zu6vzQvURy]Tp\LJ@szFS3@W(7I&OC!J^%PE'JU(eM&T6FaId64d4=H$GV+jc6@wti`f}Jwi3~6aSUXV{JLCGzG[c2Nw7[|?>erhvUa`sKt9\MOgBVv4MrCKcA@Z0EZ*0Q&BT(Zn6b<8ZYHyIMk2ds1_ESjA_eFX*Mf*5`+UqwlO~a9xzQt]C~iBnEÑ\yc}YmFlJAwN`vMPi6`n3`z8ox8a;`8hKiݤ_l7UU-gs2m};NzRd]paSfrWHa:i_bNPnD5hhDV[OAVNJFFPD/V=4~J2gCtd_WbVD\YPqbCXuDhlS{WG|MiLyVXgJDeU?SDy|H`]VSNqI[oIVf;\h>S^?KX>JU9U^8TmCHrOF`D\i6`=Ei֘pːFGYREX5GF-WY5Rb;Fa5Ic2Pr)Sq'Xr"d&m6CS`p:|;YQFtCHt5Zs=Nd.;\)UN'5V2?I#J\,Cd/9Z)ZZ.kBDcgm8gSU^Dlw=QtD:D/96|aHap^xlJ~iOS;gT?jEuYxsa[w\dYcGea8Xm?RwRAV~;[|@J_EuB4N9*<-)-#&%egMUl?05=R,^O`\Fi2SQ*ke9{s;W㊣iKOS0Kd6Uh:?_3Jf3Jg6Fh8PpG~f\gGdvcm˜i{Ri?rwH~Wjpn҄joDRdSdqFhHCJ{T}QMdxcKT`1kz6[7Wn7J\3cx@rJjMlR曯鑎܍ˊÈo_pi~i~cdmLxhFjK|VuzOwV|cox{vqtb{KxL~?|yL\nc]R|W^^tk`ZX}euwjct䱃s`Rxjz{bqHfLxZthmfu†osڏ߂ˀkl[jUnOv[_ySpPnJXr5Qd&LY#HT'IU9czZqVf?N^)Q[.P^:Vc=Zg?XjAWs=]p9Sm6Ic4_nQyKZ}1E]*AY3Nl;\m=XuDhtEmf:Ye7_v?bLu^eZpJnt@`u9Nb;L\3JZ7VnKw]fVpHUqCEpSQ{[_zTXmRooϛyiQ]b@^c9cg=ji9b`?hrUauUr]}TSflJ]fRYlU60$>8#@=!0=/1?E[F7mV=kWL>^MQHNlBJv5^J6|u3[RV]i{JVBEv@H`4MX1}m6Snad~I8\QO6ko/\YS^>g/`U3@$5>FBYDEa,0b-;H WG%]a)DzU8U4FX*Je/_Y2m5S䕓ٌUg;[OyCwGqTyT}X_[tc^}RXxKEfDK^4Dd5Od;Ht3Rn5FrEBh=KV+Tb<=rA:Y+:Q KQ*Zh6Qx>Tn3x8OZUnBSy5Gk@9c.7W.3G)5H*;M)BZ$Ae-?S$IP)}b.SO|RFNWb=Fc08H,.D(5C*DO0K\,G\:Um7wv1ORS{]8^J>nHAL!_H&hh5_J\sBN_6_i/Bfuӄa~ф_^iFYexAm]jRZyF^g7sKuWqfpR^n^kWQkQo?}ÂmUns:x|Shr_dÁć͹ȣ~xsxZIxD]O)zd8atPn4Ym@pQ]fYcY_]e>fO0RP/pc3a[CpfZhkX^hoceauC_|6TX&eY/ce9TH1liO{`ZucrAg\fVWEiwEu뛫疸北~UeTvbk|~|t|nVcIsKwsMmlFgfDmlFxxKoKsSyVZ[xyUuUZ|DMm6Uj9ls=vyH{}LVTTdfW[fdeoZ}HP`l]Rcvpjvcs`s^XYfto_nxdptaNt}?guE|vMzsBwuC}HMx9?\+9M1B8D,TbLuSm@LW*>I+KM3C\:B^=XU>_m?Ru7^g0Kd/PhN,Db>`xVraqkir\{auĊZeyLs~oƕkrIdd>eh<`l@_zJxVTyDogCeW;_Q7`\FjqOcnL66&97&35$G-$P;%=B/WC65Q0LD4QH,NN-ZY6VlCQsIXyKqmSD_^o]GvI_lTQGcTf`nuTJiXKGRNP<>??0TD3eP8QB=aX;Z_@i_L[nJje=b4Pvsj\uxk]a]bQbNG_W3u^DgANOHYAjD.p`3XpGgWHm<jfXWKCS.FW0P^1Ai>F?4GQ&>U2?M86M/F6)NTDa7zZ4ir=UYY}QEQYoJjzAhVeOTuFE\.LZ'j`#Sh4<`<[i7c{9Y~DZyN_BQI?pLFb:Ud:oaa-Kj7Yu8N|7Te5t3NTThCHu-Ef/Ao.:X/CC&@J(;U4JW'7j-1H&?HQ`*jl6``RyEIb/=?">G+Ta/\_AWc;YdC]j8Ws7cq6Zt>W{EbIaF?{HCV'>K'NR,`vAZr?Qn1Ps,f/cmm߅cfH>cM{XkQQ{RCY-[T-zCsxtpi>pVRSÍXkgo^|DӗajvܓЧmd׭l۵uat^UTckCs}GjsLe]FYJtkOrfLgkMsXKrVFLdE_O1q^>RuTwh8SrK]q7Wp3_k5Tt=[h/Z{يXeaDZ]RJo,bc/Tq2Tx.|o8sWXÉu̗uinwS\Wǧnp_KxHuG`>`{gkBsN\Ke?vYbxJAA+B9(AL::X1CB5OOEWNZt[\jۆcjrqaKsCZ[/[t;`UHqVxFaPs\svx_uInm:RS,UG,]]9spIQwD~YxjU~DcF{pўߣ}i|P[X7_L0>K'CG%Z\8OY8rnwks8`k4Yp;tr9HOi6bF1b\;\Z-Q_7~}\ґ䌮ٌ藶韇Ԋpupuޓ좾뛙vlilWhSqzT`zEcpNicgZdWose}Tx@il6Ka1Ub8mzE|[t^{jxZmH~FapymfewS{STwM{V`fYwGuQaeot{sTtQ{K}W͗ŕ}ĉ\aaWsIcBS{Ei}Jwz@hy@[}Fd|;UpIQ1GN,GL,OS5[X7PX6W_CeCRy3Op-Nf6b|B`y;K\0KV+E^8MsFV~EXv=ZiC\}Sobpv{ҠuVG@x:3XQ7k\=>]@;E457`7 Zg0a|G}}K]{KhypK]Vi~adKeuSPQVYLCCP;?K7ZE:jX6HTFOBAPR5EaCKN:O[@S[4g-<_Oq^a`aEUR;NEOG6UY=fU7LxETsCei9JN;CM8ZM4e1lVG|u-IDB-![D$Pn8-_AF3/QF$MX8?O43I,8?&?E!UY1hr:c|@VvOUlHKcF"9R2JP,Bn*%X186HZ#ov/bNDsT>_47Q/CR3V`3UO/]_5Q{MBtDP_/fb@zwLixLeQo[GQBV9=U(MT-ThAKk;Zf0Sr7p{2yy6qFYiڅ`b[J^uKc~F_NCk?@I+^e6C`}fyWj\lpMJ{\HN]yoSg\t|]˔]WmMsR^}mJvr?zCR~^lySuNdvru~dk}W]qERR3fsOhmgoOsINwDHb-Rn5^v>e=_{~\}LsaddWRc`\Pfu,u~CXvnIbkӗ\xSsDpBh?rkHyNWq}OvDTijIrNZ\l>erNuM|VZZkNS|HjPmkrgpXZ7`s:nW}cnnkbu^}jukxamsUnJl~OlrO|o♤‰ǎ쩼mwf`}YqPtUvSpxEptBYzDbKb9Yp@Yk@e_@LV?PaGcxPdCXa:S^1LM-?:.>F+BJ*L[9btHbEXt2Eh9Wp>`;Qm@Tl:IpDYzPjPbGVwHZnCfoL|eы㟯}VuTc=K[2NW6Uk@LbESqjȜoxRip;[XAgg[zvlsgseccm]oQav@^o1nV:`cCNgRb[LLYEaSD`y=D[@MP6Q`Bxd9X[8`n+:753&MN*D]7D]8ER8EL..C/87/5G&;Q(ST+ek6PGKkJKb<stLJXYVHWl3\tChGlHU[PU=Ak4I?,|M)LELoaXX.975g>966G&FG'Sf)'R-77#DTey3dAT\Pj[q^wFoBtYeZ@_NjzUz6rS{T=joA[mn]RNm@LrB_m:OtLCoGTY7`k<^x7U5`~9L˴l`}pZtnqI{UVYByQOF0NN/TI,oX:}Dy?PqhPsI{DdhtUcphK\oJW{^yHQjg|O\IsN}Plffo~pp|k[TNdlGsX]{U]x7xr8_pV`ɅfoW=f}Bna;kQdSX4vZ6pDa}Vpkis?cl8{sKT]zvdn_dY_2IG(Yq1Yndh^Rl`RyePbT~hGKVNhs~Q~Hp심ws}Geto~]bGGB#LE&IAf=%zn:c<[]YkyrgmRZvYwYS_|Ac|Ah|D[q=Vz@Vh2`_#fc)bh*hz7zMvW^P\FjQU~AocKpalB=CL(OE/iS;U1n>PN-QG4CP5:E$L@,|O=]wևfyt|Džʄ֞ӊ{€nRou@muLcxMmlN}^[zSTPlKrM`;Vj4apCmXljop`vN`D\~Rd^^\_`wg|_scguhmdwVrvM}yfy믋нop~ꏙs{oqlm[X~Igq8Vc;arPhRaOcYV~KOk@V^FfmUqWhHfiA`b:RL8LF6NL1LZ.Oi5UsHuRiMU{Dc{DhIixNm}JqG^N\~F\{9]p@SvH]Sidv΃{]q~@\w?`}:Vf3W[:d_;g`Enxdj^zZ_XswGc_UfGeWz`{`{nsi|OYi9P_>YeGgyX`yWbxF^\0QM.EHzV[RERaKjW>nk@~bxf}y}|i[`Wq]QXLI`DSXAMJ=LL>ENDEW;QV.Il-:P241%?#'Z,iD+w-*D/GZ>HQJKJ4VQ2dk:dZ9f^ByVZfM_rRE9^K4Rp=QZE`b8LiD;X=4A+5B/DD)FW2Ad7ZY0Lc0Bh>TR4T]17h=.Z<3@#@>\]$^~5lHfHHN]nJ[_AU;IP39U-;L-QJ Zr>TvPHoLHSBhO(gv?k[VP:zXn51[.:R'UM%Ta$NwBPtGC_55Q'DI'[^-ovE\HUzLMm4?g::X25Q(=HMOjX+av9He7Ac:m`/U?9_ECU%@\$JS&3a*3A7657K?!He$@R)5\=KY+g;m@v|=r{P{l?j@rNfw\|RKU7G5!fS-~uEt[r{ukg_dQfP]HVq;Ok9Up5Yx4vBi{Pbbzz3Of}[rRebJd_WeYfY_HL{:}m7r|F|AtM[lPcWfo{_{HXvJjnďVozyS_}tn|xbmŚywl{QpfW:NT>Kg>Nh.[x1Z:_0=h8/b5?O/cwIZ}Bcyoci懐njLnXkyWeEnOsLZmgb4P\4Q`Cc{X}YUz[oxRd}YOvLQlIfyGgx@gX9`U6^h=^c@do=Rs@\DcN|QN}vAvt>ep[t:jb6hgi[_\8DY>ZR9la3LbH@PNXU9Ef4X`7)R.878DG))U4H,-YF+eE6ZT>\\6WqEZlHyk:}];\bqO]`eF=^b>ZvH\^CMV:HTAGU:Ia8M\=SU8Lb;DW@Nj9>u=/T7NE%UT!Ec64X;+F0DP$Tb/pg5b=VKe_,V6KtFDa:DC/@< 2K26P&TJUh4LpNCYHDW7ZL,]l/n~HqL4]CJ:@Z%0Y3V=%tc"CaClH^[(m;v[mdwpzo}az\wVfy8r}=YXEI5k>%oe,cnDTDk@WF=rPTV5Tk6Dd7A^9@Y)6d0=D,8N#;]+JS+Yb+Lu;4k97K2;R"Y^0RISPSwMJy8-mY5V9uEr}OczIXiIMd<5H,=9"@URq.Vw9J\eUY3j-RaU{>os-Bn_Hnui^]cDdxEcHygEYC^q9dt5s4Df~aYiPioM~BL^ϟlYx\TuxK\rvsd}NqP}ey~yckksWPO2[hBbaO]]vEOM8R+71G]3KY2T[4FZ+^x0`G]QSo8LS4[h4m}7p.``A_ǚ]}dMl8Ti5kojxQԞakrNQᒎ^made~Ol^O~@Hcw`yiYlRV]ufdYR]e_larUuGWD#s9'`a7^s@rM^pEWbgdt|YȁhccXar{ꑲ}\^ig_dOuYjUGa5TU6J\?>J*^Y`yO~>Wp:qefdIW}EjwVtxQZX?cJ5mK8[RBiWBxU@HL4HG8ZKiRBP->G';<"MH+cX=nhJyVyޛtuTeAq|I{VtvԹ΁Up|Nm{MYd>YZG{}X|KoQ{nimR~^YsNxsFk}EhnBjZ>c^;Wi?Re:bnIiu}PyJXmNdy9dp2nAHS_cbdrtff^Z}Y_mdWPzQff{TiÈ˕Pjw6Qb5HY=RaOo{U}PlwKjj?]]IhvmyvpciTYyEOiAF]?S_Iky\z`qWtXnP}t?kc9XY:\m@XkC`jUnyW`sFtVnJVw@YGjGm~Nn^d_l@dh6U[9dcHolWszOgIgHqGvVKz9VW-;N24U4:Y4K];UZ3VN2`U3hZ5fg@fTwXmLbtIekGWjDQc:X`@YaFbmPm}OxNhzE`e1:D=JI2TN0aN3n^:bfKRZMI_DlZMYTAFRFDKACU5Z^8ie4JzE/`IA2w2s_mmibucHdULUJMQDRPFUQ=U]BA]FFS@\E4_P0SeE:YHJFBB[3ma2E^-ZT;Qb;>[LU14\G1NxD\^Jmg=[nOU[N}`v`N}r]WGb`i7DW*LV+HQ*Ac6G~JKjF=Y<^M7fo6=MHZAkQ"N;2tOBL8JF%hH@O6PE+>f3T1`a,[JiONgKpELj4B]8>X48K1;GCL#^g)N9AsBQj3Us0R{=Mh6=h?9O)8G&;Q"+L(0?!;=R\$Oo3Xr9]GRANk-QL)hq.AzKDZ2`Q,T,cS8xT.fOAB$jdHGL4BF3MB,vi;sQRbDa?7U.;J#J`)]|6\=yFFlO6K*K%fEL~LKg+df-s1m[jzVg?zkro|LlZYwBbv[eiEnLp_e}Y\wvQ~iFyjyolTehVekTȎlLRLaFN|HZzClzFlvNe~ۚ֍}hfYqr苇`n~JcsMgiJoajudr\xt]p`z\|a{^cpZb~L`cFVV@XK=YP?eeKitKralWx}FqtD|TfW^IdZ[N|oNTs=rFeluVbEn}TjZOr:RS4klM}Xl@knIQ7>V3IL7[T(Lf.'e6H>(E z?{fTfR^Q\T:fc@IkEP`GaW9Q`@IiEBhEO[>Vb9ne:[oCFcILR6LU5[I0_K2N^<6nAHG5D2\s=[Ym`;VfJZf[quKNhTYr`t]IzzD_x\SWOQWBEkDS[K`YA^U`1DU1:V05K$XM!xa&hHD[EjQAVGa@0Q{+:K_S7ka,LBEuFmL58gB6HAIIH_3^\7RDFaGTMRV+Eg9m\8Z:U~Q=uC;[ELW.lqAdOqljlxU{OVipVhIj]Xl%A};*h8EA0e_*i{J>XDW?H['1[@1A&CHIN0@l/La3NZ(@Z:;`9?\.BZ/8_/@`0N_(mLK[IcGKm78X11J%*=.9=>NW"hf,KPGl0\g/Gx;PV(KL$6Y,8A!AO"5L%-G*><XW$Or2^h,iBQHAq3ES&b[&Tt:J`37L%97?*G'9TW2ZP*oEXTRnGVk?kb1p[je[RKl1PU#\Y%lrCWzEIq8Rj){v>fFgo\_h5o7nS`}MShoB|e='ONZs@aaCriDrm@YoGnNk>aufnM~JZtR]dgqSmKtWyht][Ia10I%5C)EV:UhHa|O]CUc4K\5Q`;Zj9lg=_Q0FS5CP4iSASfmJOyIrW?[3Y_klmuHc}ThRV\XhX^_pS?Z9~iJt~P`US\dyPgN@zRGN;?O%EH.:F*6M.JH!OOfS'ty0dO-pDMLgZ1PEK~LZT0iP'Yv3uJ=kD9TESU#Ea>WN3nn7O]@vbGS2Nl6Hk^p:^pKB{G3W^6`)>lNWl4Gs3Ve/eGMEGw0=^/LQ&SV#Po19iE5O(DM)q_5BS???$A9"h2@rbcXoGVc{UYsGGhEZj:]rub)Ea"Y^.Wc@QnAXyRk8Ib=OgBGf5?X1A^CGoHZxM[rHXj9M^5Q^9\k=bl?dY5LU.PF6bG23O8A=6MA,VF2LG>NPGUO:`L6pV1o[6EM93K9:R7CU1*M-WB#~[%D_[evfMYH`[:QDTG?0N4?84J@,UA./J;A)-=#:+":+ J.ZD,KO5[O2^f7ae>koCyIXKHsRc^Aq.dRcqledBbi?}h:}LWlbfsZdZ_YLjqIkzLkPn\cvTPyN?VF;N5>>.@=#??&1@$7>%1?DD#iF!yc&RVSv^hqNoUEXFf@MR0lI*q;DYIp?Xh:gv:_`2bZAQ8HZ-6l0-K*67AB >F/2A)9I':J#ND Pw1DyDQjS7ZFSR*_IS(T2_eqHdSLV;X_,Sk8\UdUhi;Ug4cY1O?_j=Pl8Sn4V|3Kq2@X&=8IU,HP;vO-nM~GLqf_Yf6jt;{:uGAyUxeSuUYkwN\q[rNuOlP~IlڡuWn:wuFejofviY^w>^a?]d1iuYN3`~=X0>TC\t\qk}o\g~ISlRd:[h7^d<_X/D8)]7(CK/9FIO=@F5ZE1TH4nS6XW4T=-E./>&D:WJb`2zjBO{[ng`oCTSKH=DI+`G4EH6_A3&L8'.-*">!m4JQ0KO:ND)DU+OW3Ye4Ss>V[>LT5ie@ws2btH@THKS7e[/`,zb=~fTXhrmo`d]e^rvO^~[jwVPmGMUK7W>9L:0G/1C-6:%9;&6@'7F*:K0w>$w|2vzWuUcua]|OHDB`@TN1mr0ZSIT4Rd3Re2brCI{LZ_AKv9V^6{e3h|IgQN^B{bAf<,h45;"!]D!W4MKCk>i_7SNLUF_CDW.7_*#WJ*+_<\m1Jk;_f-M1O_>Lt3Nr8S*wDN~w?l@KoCXy@`Nd<[JeEPCYx?F6Ek0IW/Y^2al>ax>|nM[pFalEq]:VTnwQhdmjmdnCz@w[x_i̠o}_Ws9eZCm_A[]=bQ.Vi1@j-RT&pxYv=ChDBa7>\,Lj/mi7nnAivAs\__n˩݌epr`qgiPPGBw.HSX*el0Fh3jn.y=z@i|D~IbMf}DLj9`L*|}>Pg8TO/W]7UT.`P)_9siE;SGdjƞᰃٳ͈͜}~g}]NSmp]W`PvT\jvӅ␵sq_`Hb=Q|^_mQ}lOid\qțxv۵ҭʦ|nm^QTi}ZbrMYb?ga=_T7VV?]pUp~̀]PlӔt_koqƂyYsgKt\pCnSwxZwChJsQkgx˃|„y~ĝԡ•ĂuVN{{K[z^^F^f3``6X\2S[1M\)IR.oe:x;Zw6Fj+Ee,9['=Q&La0\{E_m`iPVv;4E21E-/N,*J+4;"OES[,`k;}pFKq[@k6JR?97O?&PS1L?2<2*5+&1,;.16"i6mB PJ)HE/CI$EW*S{5JsAI&9Y4kC+_n6}dMq<]f`t\bDQEXT8`m=]|Y`pLRRIpKQl?SLlwKVEG~H;b5JW2nj3~7otTk>wB?TU}@tZ0o5T~ZR^<\R2Zn3Qk4`i/Ts6\~CRbBWu2R8Q]/Ck;I^6[i;rg8rb4V`CZYAQY3JT1ZS8nO7o`;bbDU;z[zVq]iAdDaTJo]qmPeYeb[qKb6MTPk8_t2:IÃKuSWxI]uFgp:Wx:oz@jN}We_PVqo7j>Qo:a[2xi>WZ_UzgGoNWyHj`IsmRps\qce\SyOfwIny1or?}RG]Y^_t[mm{xn:mo=]n3B_6J_1I`=I`2is7fG[bCccFuNjQUu=PQ=Qo:Sw@NuPS{H[u@dwHStcgmI}>rbl\Z^<@Jk5Fr5Rf0_\-]Ml{kI}qEud_nAYK%YB"}l=|Lj{P`aGedAMp6AT4^lBcqJlߣ豤߰ϖ~ubo^=iR}]j|h{ltm_yW}bz\ixɺꡲjZgmx{Wdjqwsqtȝfʛh„SȂbnjc\OGKa`zKwn=\`=dY>vsP觇nfRaaڠڰ蹺ÚퟙӇʁw]EfEybZˌX_dT{nBlm:rL{n}ǎЖןŐÕvlar~Avf?vi;gjHWkLRn=GZ6^N6YV9N^5Ei5Sn6w9r6^u0Nn1Mf)=T)>J%OP,_YLX.?J->D%=?$6F*R;!kM&nR+S9NY?bHEMP?>H@Q;1MF,VQ/WR6=U?LU?CI6?S0>Z0$[.B='PMOb/Lh:XpBr>KK]+i\:I;OM1;S6(I2,3&*/*9(> +4$1:#7A"1`.2K/KI!}U(r5/P<9=-D(@?+5Y(Ia4An7Ua3;N/KU6hq2E|Sqhw_p@viinh^sWmPnYfyQ]rLPe>?N=9A.E?+_?&6M3:G?Jc6Hs:|fBV~EZg\ud9gMQgbuSZM\~IcrG_MrvScLV]kM]Zcs^ONEBfERc<`r=}r;k^UmQbB:m4ZK5sv0M`l^Jre>d_gpcevg_x1}6JF=P)GY#Lb3Mi:Ce:XnF\INQSnE[rBTwTpU:{m0rNOmUO~I<|D6N=>R1ES3AK+IU5Pk9Q\:nL0`AX]eD@H9T7O\&Gq8RQ'TuDMjIf9Ma&4;MFMj)VX0\j3LvB=P4RR&V~0O1JjAVb@Wu=8jIU<'\N0Nq>?XETC)q]-hgDghETQ7K1g_\i\lT~Ru˛yaXqrEss@mTwzT[2Qk/Xv27i|}WopEa5Tn1Sl<[kExFvZuGgO_RZDR{Fb{G_|kmsb|SmN|OPTPYrUb>i3se9g2dv6Xu6vc4`h7uoBV|jv@s~?{`DtOprKbnTZ4_i.Yl@[?XB_sAi{Jw~Q\Gbw;MPʨtŋvy\y~Dy?WzotiXAoJ>W|H_CeQWdmxrqL^F4yvUVzTGg<V`RZ<:[;.F3)?$5E#Hl.=};IC;=O7aP1Gb2:T5?P'KG$PR%+iE&y[;\m@hO`NSxMQ4iBRxkfVBnf:kqB_^_w_TJWd;`i:jzQhVtgwnvgReXcRPe7T}H^BSYHvj:{LXnAdNEG,Hj0sg4RZ`wVX|lnsuā@CoURr;UFaJbMvWi_QMP\8V]68oK5E/V>kJSDt|UrEcZOVBsaGQDXd=5S7-.#?2jBpx2a_>U;b>UW&De*fU.h5V|FzZEa'@fd^"~w8fT^M=v>> 5`:/B+77MN%Cd)E^1Yl1@q>@M4PO%[0qDrj`}jl\^Ub\```I^_cN`yN[N`sGUm=eM=u:|usoql״wյwv~knoC_KdwBkwEaVUPW{-Sz9{z2cJDb>Qd$Zf.Uk>TxQOXFz}@RhNtEEohk`짓fÆsLrFbRhYgPejA\bAmjBhpJf~B`yHaTZyDnj2i|M`ɮunaStdhmU\hEbs>Ne?US-La&TI0hVAZbCnf8e?qvA^lAdxJjVX}I`b0IZ/BU/O_*pi-g=cy>^iS?Dh6E]5`wSSuIYK0fB,dW1z6eȆْ։ǃl_C^_3`h:gCrqd\zXzSaƀmnzMsM1d=tFT]bvtSQYvmxvnzjghtYhyPQRZӓ\˚b`fpÁԇogrzqmgZ`CX}E{Wywל喹ז݉zԃ[[R`6uLqR|>d4Mh,LV'RQ6z[nΎϏnD`>kQ]iwAYW0u_?zlBvmCir:Yf3U`5%E=,IL4RP=WuXi{‹`dIh[m}qfmouwcsKn;qG)>C+4>*.4%#A(!9%$6;:T>#A=!(6#=13AM=%PI&CZ/RY8VP8/D8!.-E%"'K*G*$9 *1-6 O= UV.Qe7a_>/mD>K4IL,4L.*I2CC)pL%r7VOLoJM^7>d.VH"=M&5S;SB(Z^(LoA9fP7P-RC$DY,FWBFT/Sd/\u3M|?QgAVW/tc2bn3WtIhm?g@VW]rI[_9ckDm{IbKIuKIR@9U8a@)uXut4j{AStEc^6G=YBf3C]ZNKOX;oJ>lo>WVZ`^Ncl:f{Og|wbNdQqJbi6\FUSD]k[?{GbqBBFF4[T(u:^Og^etMjgHbf@\T;ZO0k9]UnPqsw[nZbTauB`c=Oe:E_5LM#Q[/:vI-R2.C)A;"h>!g;_[yMpuG\tV5wQ?;%/21;=jF"n5IYBhGBf8>R.TP!e0Y|^HiFGuCOm-wj!hj1vDkKBGoK4Ee>4T@GF)MZ&I[-OQ/]t=;iC8M3VW({6[us|gyZMfsuuГΉnē{{m~_NtmFRd3c_8htDd{DoJrSXRa9W~0p~6hULKKg7Yo:kt?ezRcuQvTzO{VlEMdqpž{ܸˉٝn|nPt9d9bq=l|MpXrK{FxtFr9yLaGSʆtԱo`SkYhcAZ[1^Y&ZY&C^4O_3h_*r^=`^4]a8pi@iPYrlkJX{HB]1AU)CO&GE"Va.SY)TY7Xd>W^6LU.=P"M_'Tv@Wm>XbAdlKi}POw?XTEpUoa]QT|;Js2Zp;_P`K`>Zt2X|Hs[rU]9Oe3P^;ZjIs^VgIUzIn\ydteqptvqYzLXzKddukc}MwLpMzOiTh_n]g}KhvMdpEpoDsNSTrP`PP}HV}MZ|A`Df}W_~\{]Hoy:Lj3Ql:]y:Uv@RzHa{XujrsrnXZStJ:`A6M85S?7VB:V0&B*&A#*;#D81++K4-G%9A-J6 Z?'PR/D_4;L7&=.E/(/U*)J3*;"+?/;!9B$JM%JY5E`<:[<6Z8GB7L3'8P)HI-WY(_l1Os;@b85K'8Ik:JD,>X>JD2mG#J_27iG9Z2HU!8S*3I6AK+V])Sb-Bc3B`@E`5QV.]\0N]/hnXN+yq0lKWbFdAlM2Z<)J4L5)}m2aXij?eIZyeTu6VseH`z]y'{2r]]Phl-SI_h>eq>oLfOrCgDDp{cpٜ߿\_E`x6b}2UAyTahkixMgj<`r:zr4`UwHzmUszMwpGnyPKeET^@VhB@n;Hb4WmQot_riLa]=CN(E>!`C$w\0z7FlWk|IsWhPtUw_ZBhr>z{KyZaZ^f/F]-D^2VV1W`>}i:r=^|4Ur8Yq@gr@rn>wHrϑƋtsUfK{Ysa~WpMh{ъΜ}fRlrAB>%D/S9#EE&04O<:vܚa^Jte?psWzX]vY{UsQ~OlzAgh5^c*qCRbcYwIvGfMxX_WKN|J~QzSyZza|li}ZZdStw{xeVLq>evR}i}ɓԇfy:Sb5arImRh\{Čs{JWZ0X\)Rk1Li,Ji*\h4^o5^t;Qr=XzH_\g~Qd|ELk86b7HjDpYyVZrALi;VlHblRhfipyt}rhc`eeRyAms4Tf.IQ+U^1ZwLtxu]vxO\yK\vF_tJjzK{|KxGp|R_zKes>Yl8YjGxzYaPey6Oa/Jl3Jp6Jd.RP1^Y9UbKZoUi}`slvqp|swrkU\Rt]UwWXrH+>%,:%3?"D7%0:) 7@0E'>@"9221O5&QF3WU68Z=_L:1d:2Q<3>%1G#1K(FD)Q_8IqK?eMHT>2b4!K-E(&DA!@H*IM)Mc*;T+/J%V-FX-Ta&BdQKm=LX25P4=M(P>#DN%BY3QG*D(ha2W_]^H7wQ3RDDS,qLukTjm~|jhiPtBZfZ6~y2zBo]mAuTQK9w3UV3q{7{Xa_ObEzRDnG?jDVfAr~@_WicuUzm*JNRsVLl/Fn9YV8Nj4An2Ea)Kb22n=+M95;<<C?!\Z'o~;mJQLMmF=U4h=*E&}_4ZiLJbANs?>g;<[4DP3]Q&o`4fsF||RPYA`JNZaq"cCXlHsG-fe/t>}i2=&oF(ZG'S_(mT)kv9fGj{JVqN\sAow9h=]cAPW2^l1[Ars@i+KscmXOdpj|XyoBj<}~DDlJ}GmV|lr;wC|_glhd{VfvH^HR;wv7oORUUvD}sAZO^uIenAlv;e?_u;Jv֢ԩozpDZBKp2V`!mw9sUS~UfgBec9Zp6l{:{q:aaznOpC]lantMiY:wrIiP[bhtUs_nXn[l[}kd[waGm{_}S_sIHe@Qg@OjGKoHRk>X[84c*@? HU$HJ%LF*?@(GJ$FL)e@)hA0QeI[Pp:BS,K^7Bb5HX'IQ&O_+Lj*D[7J]@LgCBpG?rC`wPm}_uC_o;wzVxtvz‚rg栱}addBKR34>*7D(DS48Y1/<%}KA[ǫgq_jAmlM]WJtAvV]tKbIryE~c]`\uKKP*DD#hP0RgjtWiEpKjJsQZuTvUlhntzēƂlXgQzcqYeDW?]Mvv`RfeFjgCsxK^pq_tKS}Qj?XmE2aH4S:=B(:Z%>T3g^5UQDkaE]KZ>5RH2I[5NL)LT.?G+>L'1S"/D 6EFK^@$TV6^bAFqD@^Ks>=i8FF*;K"PS*]\)ZvDUdADb@Wa5Rw5SrBSm6L`;?d3Ha7Ub6`p1J11S2;T6drAVUiuJ`FTeDOT.GB,^?(oK&Ib8QO&Qi3EV+IV%3V0HB*S+gqDx~TVWAhN\i7j3m};?rK9?7U=_n*=w5Q6&>D$:?"FX#o].iKcqKflERGNqHepDVCJg;Hf=^n=WGT}Dc~Hzs>sC_Zgl"@R*\qGf|M]TRyDHd6G`7HX0RY)Jk35[(?V/Dg,:Z1F^abKk{fnno_`NnQ|TrZV|D<^.BV)@K 8J'@]):V&6N"CU,Qh?i`iyTgX]WL{>6Y&.9:;)WN;ssKqEoB{}Age3HM&2G"1D EL"HM%-C"6X7_ar`q\oyRmqDggQeZp|FYa5Ug4eg+Gc$>\*>b/Fc9J_6NX0;K4FR6E0:A-6/66.7!+7!$A;$#6,1377A ^H&VV/G\?VRHVQ=|S?NjE9kL7W@KK1E_2joAfJ?j]I?E7V2)B7F4/KS+Ka3jW9>`?4Q<8F+7F#O?%ST(F@.DH65Y(6K*2D ABuR%Zr8J`DH^;Tb:RuEOl@Hg1Y\6Up3ax;Zv?Z~@s>~Wlb>cME@Ui.Mr>9h;;K4GL)Fd+C\$F>#==#OW$e7]UW}MaITnB[[5dhAK[c/om.kp=dnDZl>Aځ̐dWK]g=]j;I].QW+nv7xDM_Yv=aHJd[KlE`LiLr^uPzQ}CˈH`]X~;gv-n:\yTmp@ul:gm5in7nPyac]~{Qw]vƔ‘{n]m9_r?OhCq[@pLg{RhmYui\rf:l[\~U_T>LL6NB/D<+C2'M6&Ob2Fi:>R*/7 ;3:Q)[Z9_TAFVEE8+MHoѫPWfrENnftaOwc[zdOzcPRXM;[aL`_Kj^l~@Mo5ai;~QFJ|jBjl5cl9hwBQo@Lk;Gj?4I)=4)^xSAI;W4JMPEaG_zAZc5VR.AP:maLwjwl]Zd;atLllijq]oOp˅yԏwp`ok©[ČRo>uzGdK_Q|gZKt5^f;bo?nPr][PvCx]5_J+LA+79&74"8B(>R*:X&Qd;Ni_mOlRtzpqafNclHlqZyn}Ύǃoyrei_akb`ZhXmkukcYyOpDd}?\{IXyB]WmWqedcQ`OaSaisqwv\bJpTm]dMOy4F^&>I!Z|Vzؤi\tUfM\HF|<3c;2];:];UkA`k8_d/bW/\_6LY38Z/3N'5P'BV)AS(]jEqiRjhMY[4VU7HU7AT8ISH_dUg|`cbQcVTX@A_2/*#.&219&A%"< !2.54JBH.YB,`K2^L4JM6]C:^S3RQ@>T=>X5TU,Xh1_sD@aD2F9R7&@\.9=7D@4JI+Qe:Jh7SR.Kc1/^0DF(JFMM"PO6bO:-!;E"-X&2>@AgS!LlAN[:ZV.^_zRKF:Kb-Kl6Bd4:h2AT-NY2Gg@kUAhnQdpjdlS_cXb]sKTz<_MaNK_O~Bhp1x{EmQk_RVCgGQ`0Ko5gb2z8|kodz[pYUihqUkWlWjRdXgTRuIRa=OiA~\GaIIqQjSWj-C;@aD<\-/C'6>!R<%bB#[:\v7{QffaO^ALZ>ER'ZV"Iz7fK0BQ4h`1Ug.Q[:Uf5`n:fu=Lx>f~BUkqlsS]XLo;Nb3E^*ZS*ou;|LlWX^{vCpq7bwA\q@fvGUut_PPxEph=ay™PdqZx?yYOIZm)gt*i2f|:dEWo?ie;]v]fvAjV-I2I6zWёx~cXAR6WbCTU/xO.gBrzQ^yiykOfҍ|nSjEUY2tIp}`^CU>)JB+BC,DD5F@/Q>*^D"EQ'aZ:}]@gRNnYVAШd^R}RbunZjPfN~ZEnT@`C4WL7eM>SAqeQkvJvSsaOitUzuD~NV~HV3PrzuʃaqAv\\sD_e9ilDqO`pmHf1Ud-Vh9qs?tOo~Ebw?Oq@Gb6Re5Yp6TwBUwJCqKOqlɌ&AH+E]8Cc0Gk9\|Rg`nvę曻܄h^oPZP\`ab[l[e_TiKrl?PS+NU+U_6ppCgkBJd7Gi:Gg;D[2Ld6W|IY~ZgV`uV]fNppXz[pWyIZ8MmRw6Ki8A\<>\AM[=Lb9Ie9Nj3P^9ZXWyqlWzgBo^UdUefHUnO^qUcbvdr]hW|wLki:%%*;(8 /5'?##:#27"MC'a.1C6:=(7?'>@&OB(F*8S4@>4>F'DQ&[T,Nl22gA/::2,"c2!E_3DO;DG6IB2]M?Nd=OT:9W.2Q+BFBOeIYS*O`?0U;:L2IT)Lf:8kH>D3AB&7;0.2,:0 /C/J 8:sHGo>DZFRN(UP,Eb0G\/M^(>S(PO&Ri2Kf1Pd-^]-Rg2Pj;EpAGO?BM+=[,>O3BP+Bp6Nf3=a5eD0s`0UuQXegna=lSQgUmHfq4j?^SWRVKVB\wA]q>\v9HxCBg9F^3FU+_a.9xjgaf`vvPmT_b_WXWpIaTHU@fNIZ3Dh:WZHk.UeAj`hJP~&BxL9_:.I(5B&6W*NN/Ap<7cPFX5Ot2jr8ZK`YSTZzIRPmUUkAFv[YxQVXF]@eKBa:Vk4_^>G<>|zwmWsPOI5QE"\> EP(\T-^qEL_^{[E}5EZACH6hd0jE_IVwKVcnKax?ruTz֐}j~yKRh8M]-Ta6ig6ppD^tNXvFT{JMp?Nn=Jj9YkAg~Phnѐթ@P>L$_e=Lb6>`5C^4poAbdvYVGqDJ_8d=Lq~Ct}L{Octm[cزςήazJnu>nFY}DVb?iPl\jb]|RstUnZ\ZeY`W]]ihkbvanP`}FhfEdR`hEI[9GN8OcQ][}abgqedOe]޴跚઄ЌSeMSsXitln]zjqtp{ɂtl_P~JO{PX]iihXQ^nF]o4Hj-ES.X[3ZZ/dU6|gHyVXZvRP9W}8_;St0E].Fb&3V-0T0@T;[i;Uw?aZyqwڑuO~HoyAXq?bvNgRj`o_mRdm>b_4\S)KL-JZ3Yc8jd$+U++K:2;*-:<:D9EB)4S7;;46?)EJ'ZQ-Ik2.k>?G;AG4\=*G_39cHLC@NH/QF8_P=C*3R#6=$4B!]L"KQ%BW3H]0Mh>aa;TU65O>@=)@@1A',0&*,5*+<2=^DOv9HhKES7BQ.JS3Fa4;=2AE#Hf3L|GKmFJoEJl?@q;Q]6MV4J\EMaHm_HtJCv\LaLS[.Zp/Xt8XwDZ~HUNUCH}OInEQl4Ff78[43T4eD)h:\gmfAVDWrZpuTq]dV`[oWheNeAySMlEGnFIqQzrGhPQmQahuAK6BhB4_/;a3KnAYvBVPP[^LZVePsSphnvpu|ut~Ս}x|u^zSb;T^yia|gYn>kR6?04O+/=#/8E-e6"td5hklO_jKU]JloCf@eyLZtWed>`qFoXeuuC~@Ub:g^.x:\zEEcCKZ%\l8SMmIhn@LiHSZ+[hAnO[ehi[^ÃijXvjA^CiNhNfhBej7mD`t:g]izKL^wPQ~܀hZgV0bEVk:McDgQ:qX[^U|1X|+}w7n|v}i[C\U1OY,RW-jV:paM|Wɨ}sa`Ffd@~[e^y[aIWHjQv`YH8uTjybZYNL2VC2_K[͟BNEIe،䥸d}JwQNɶk閞oSg]W˰ڥᥱ⤛~mucsq}yvife}|wÀvkjipjvirXi~I\x>UpCMk9CX-AM%=D!38<<(HQ8PqDYOWETu6Hj/Ux9Q4>s/>e=PdMW;aCVdBTuGe\tlneu?_v8G`+FR)D[(KW.PV2Wh>_wC^o@Xc5H[-BI,WU4L]/LW-Y]/mc?jo>Qb2DY+Ff6Vy@\Mseq}srl`WSUyOxV|J`u6BR)2@2DO@acETnMfv[j}M5U.6&$*@;/cJDQ\WPb\L^dLdf=le=W`>_l@]pH07%+! !*%34"8*5D45E'%K27)6.E3]:"L<.0D//6(,2%@; QP"Rg0Ae9=^?9K6HM5CL3L=)9N)8@'9@!;F&?F dD$NW)>T@AD60I),F/7C!.G"-;$.A*,;+#6+..$6I%rS&L{:LeRFoFKb=I`;HX4KN&BJ*D[3Hg;BdUtCdQXh\aiWigkieVuZ[cQyI~iAqXZrzpRcvZNuGOj;Wy@[}9Rz4VIp^khiiZZ\wCpQrJnvWw}uz4k6Lk>RzLpuHl_jwͪﳪՎkqQTuEdTrX`MirLx[cekoX^⠁Ѥ|c[\TzO|a~VSrOqzI^lAJP9~pUww’Ĕ~zxppdxc}QUd~_npCW`5Uc>ZwN]zSiY]NAi5@f5uZڎ䔫qYfz~̜ئҐbt~Gn{Rv}}owowcrL\v8Yg4Yg9Vi9Re0BV-7H(.?&.C(?S2\`?xPqHRv2<\$C\,Zs7Rz6Jx?^[bp|GXsP]\cirq|caK^b1OM)=L 6@"9E9"64$22#*8$*1%9>=BQR#RY.CY=%9C%LB _O*>V6GY9AX6/Y;I]7Fk7;\5=L6>P7=a;D^=YjQ/NN(RZ8L{MVrN\wLKtNEq[KmV1iM5HMO8&bh.h^js_t\|n_zRRQOOYyC`n;gdElT6GlH8`X;YDLS&Pm0Yy>_H]V^nQuMNx?CyF:a9?L&Db*Xj.f8LeQxYiH\>MxKguL`UqSyYgY~R``?dDbGZ]0ey@hUQcFxSVv@^IhF{^urplznw_qd^Xh|MnUhPIgM?U*<@$7BH:Te,bo8`zQVoASvB\nAVHRXWyDKn9Kb-DL+^K4Yz[Xj\et4ZEWFUJA\E@THSL-fh.}v?CyPY[Du@M_;PX=twDRZO~M^oLft3E[6G[/e.q@gHnNh\rQ^ShiRk@mTT]a:iTJA,Za4='w9yȂnpYzJh}GSn@lh;yDi[ZDVv?Qn8Zn;MnFsI{2q^Mq:lL$j-ѵYn[\^1\b*n;Ub̪jƅb_`_wFhFUW\XpG9N5dB-pd>mHzOoqfnu|մmd~iiQ|ZroMoMnekTyZ~[~bv[~G?|^\MvaMfO>Y4llBnwGetH\l6okCJgE::5>C2TT2G=&fK:dlinYnЛΒ~o^kkXOTdocb{ݚ旒m}m|ݓ߇j^EB4dgJt[}krwBK_dbywCgVeUZ=m?gKgJuRr̍ōvtRL^O-RC/`XT{z9}ET^0mLe䄄݂{e|鶎ǖVdBSiCrmKxKUb}Mqg@dEbGWidiÔw`~rQxNuP~k9tEmNlOnxFm}HdtImdEVhEi[JgsiŒː|xÏyr_puD\X4WaA^WbhhfihPYU\moŇݏ]zLS_tȚ⫬ݗisIQnB^sIv_sx~oxswTWf3MW*OU2MS*E@&IC-FQ5I]D5=)4*36!D[0Eg2G`GaZbHXi;TnXtc`\_WgF\b.@C+;A 5;,/.62>%EK3N_@Ei?Bd0N\:irI]{IInR):C$JJ,Vg5[?eMbkaOk8Sl8apMSwx;X]2FT3GUK1AW8=lc.wXqyftgr\f_dRXIRSn;R^4UfH?nW8e[JX4Tg1Uw@owDjZUpJWPs>Dx>-^<:9,GQ(Wl5j}AYOPb[}QXOW9bAS\k[vVbgj[vNPmDz[O_A]m>j}SdY\inmsklxZopQhFYr[[c@NsFJb@mwGp]KY2K@8.7_,Ea<@e;Kh3iu7Q_X~XSLdKbLQ{GSi=E4@]7JM-iO._xWEplHR>OG$][,ck8lx?KuEL_5g[3`GuiCxf5etbVWnJhnQD|idjS`n;qiB{e=mwMVtUyK^TOF2PP6?P,;B(bV=sOr]mVnԦެsq?V|dgN|_gfՋ{Fy6L_zӋqzThjXV{NyF]e}UpE{cDCk?c>{ZwnrivឣِrRwD.9-) ZB9sre}RUvDOh7tPjWftq\j淙l_Yp>esFryRnzGroKpoG_]Eof2$0:L(?h6FnO`boJLn>Vsboac{Cek;]V5rS8q`7GW+;H&38 .5!-6'-C'0F.RjPe|SW|?P`3kfKqXcHTl?If;Gf:Go>Gn;VfY8CbDPs:]~9fz8gGrGj>Q~=Kq>FyHNh9\W-EE&>O4E]GZuI]vRbpGIa;;U3AQ6?B0,8%*,$E7.;8#3@*8K)5C*1:->H4GZ=Ua:Oe:@`BRp`tl) !%$ $ $)"*,+%3"6!&0"#.&)8%B9"1M+#?+&2^)WO$6%>E/E=*\T,jO7Kg;[iA^>OU.fa/PyB>TNDX3)F/8/ OGNW)PY8VX8Pb7:S3AB*aJ$AZ'NG/hE&pm9pX\tUjNx\9pZJU[nE0{y:vr{utj]rSc\YV_WY]PNd?pNRb}U_xJs|LxMhbUbVQMI7vH>Q9Od4]Oj__ZXUP|KY|HWF^|@c}SoXTkhpqxkoqJsFeKQssSP}WB^E;J/JY4BE1QG$Y^&Xs4tu5cJLs=Ic.dY.V8a{=lAIa;9L1LH%RD&mL+eT.^rDJz>Fb/qj+qA|CzvBtyA]CXt9zKz^jJnpiu9Gv~mȑlhV}eWoGfV7~o>pNQqJah=X/PhmfG}H:W$[vHˋyrfARdChmAziFYLY^\s]tSlngRroD`AnVbGV=R<]BvUtb|lzx]wPXxầw޽Ww[yv~]UpgJqX@Vr]_XJ}cNwzlq_>\r\z[~w|uR[x:T`39U2Ig9awDa~LtGl_g˦zwLt7|o;SvHL\TԢi\yQhVxg}ځ}{xcSe[Jms@qoHhZUNG|==c7e6dYY9WkMmaup~NOUxMvMuXXOdH}Rvd}[uOztYz˅{wzɐךԒĠܣןՖ|bvHkVBxLhQ`zD]xHd^ns~מ⡭ۗmYEe;UB_W{jmj_s|FXi4[cJtforvk`UVnL~\}\fu?BQ)AB1Y]JehC_TQ,0K&,;4J#.P!%K#3R%>`:MvZjXbAcqDcxZuelKUsBHf8CO3UO6_f;XsABa5.C#2>.7C17N8I`J_{VkRes>Qg5XcA{uRoIUv=ZxAUzIXLOsOJtJJuSU~^T^XMT|]K_RVKRPZ\XFCh/1H#/D&@Q9[hG\yGHkG+:E+;G&A@-AI7CN4B]5H`>Rd@GgNj}\"!#!$ #*&,(3&6$ 5!"+!#*'-7,Q> 5M(#A+1/[)=F' L9-(1?6"QH*=]2KI1Z]2@n9=M@)F.86,9C'0>&-:&;%2 &-+,D,G8KH$WU,J^:EG<9B&LE%kd+>IRVD;[3.@39?$YM$Nb-E_6>N8KH29I'=D&=P!=O#8A%P?#gC*ka;qr_smcsP^4rYVEDxb+y\snpngij]j]wiphjlof`ixrgkarUyTkOzYj]ebfSbPbMaSqM|hzt_t`\VFXB[CWF[EfHNsVc`lrhoCfSnQcx?R[fnIfG_POvCKG7Hc+=b0ZR-OwC>nM9SB^T/]xBXy?.kG5C/FX,fq=iMCdNfAls8n=^`[sGfzFrLfpGQsbGZ]4[C]\M^Pe=Rh5:^ODK0AJ,PW._W)Vx1Sy?Kk=ag8mwLV4EU0SO,Qi6Ye6|Eij^{tcg`W`oX1|yBpOdfDTc=`R4Z3?mKZ_b1SwgdXpcCuoA~iGh;oHF}KcGe_2|AjhccW2uU4lBkKzfKfbSfJmZnX}[ʡ}rkVlGQk܋lʒoo|Uo~qzcNvqN{c{WUuyTflMbC^mrh\Rh͇Ӎx؉dRSf2Jk:KZ;ozKeS\zLf|L{^̙ܩzzMOV{=|J`G^Z+]P+l[8w@ly8tp:z@_ԅчވ׆qtttEzBd?rL|K{[u^oUh~N\~cuFtv1h6c5Sl,\h6bb`FsrPekG\[DhkS\pJTaBTgGvyOnF?]8_PL[OQ]KhHYo>I^4EW8UfEjrGXtBQtEg|La]d_`R]|KmmPojDjd8TT4UX>Vg=OgEtUx\dPgXrSUi65Q&/D.J!;K5O\:Ud9BY+;M2G^<]kCkj=ph:oj;fg6TZ3QV0;L)7D+FE/QU5ES3IW7@_AGhFKmJ$&$-*") (#$,%9")>/%7- 0(#-!&/(3 <6 ?9!@E%->'1/X2LL,-S<:@53D&=G'=L2TN0QZ8Kn=Ja>7T?H<08;'(8(2-.3)9"$/($-"G2SP'FY4(=665$>> P?!i[*Kw5GQ@BJ9CP2HU0N[0?N6K<-=h:BOFDM-=A'6J(;L*/O168,G=-^<(_a;_uYbp`ueELIXNtY4ItgY^ZVmZrhovli^\lqPy~GoUuP\QHTTqMjIwYdchSf]dZqNkSH{^wh\_R~ZOxNUzCV{=V;TD_Tehk^laix_n[^aFdJKRTlN[o8R{A9Y966&OC$Hk2HV(Yc-Rg?EaFYc;GeFUe;=|LE[7OY4_\8D^lV\SjGpw2rZwjhwMJW;EK(dT)nk1jkR\?WwAgIb}>sk8D_ceK~\}yRz~E_iHXqBqm.aq&YM.kX1X\DNK0I.wDkhSlQn+u|voI~ir[@ch@]\:bn6hDiH_JZRuy]}eYzR^R7cE*S4il}}Oy}{eWf~w|fM:{]GkIғycv~~f{[z^|]ax}bmiLxgTyNeӃuo{{㝡ݚȂ[rKK[9Xl9WPuUeO_}IgT}ipАٝ횏llaXoPcSR:ro5NNlAguEjfAf뛼쏱䍃ꆉ~cx\tYn}gpUZoibRKTs4Y]'Q^0TE\PaGIq93?":-Q1!hh;dPkHxu:J^wETgBgsLhJ\B_9Lx1Ib/s~N\vVNTG[Kdu;hd2`m=]o5aq6gk:jrNuǮӡvcc~zhm`pmPqwY\unpۛ|x\狻ݍ֎˂{ofvRs]sVftETtGd~L~nѐцȅ}{[`z7>R%9G1bvDgLpGuPoWmNYBQh-Oc?RrV^XuPkPnaq[Vr;Kb/W_2QV5^F;r^I_IbZ?AQ>PK6smL{TXwHEi@H`7?`5BY2D[58U48H.?H,F='MG'99!093UYDflA@S=hjEnxGYkLizUWI6g85S+/M,6W23[8@U1@T/6P28Z=F`@Ga;KbGaTBK9IE'JH&S@'QR5>e58I2LC1[A-^pFOy_SsZPpDSIas?\|HUMTmCizM~_wvazegRUUsJPoHOqX_XKStKPsGZ~TkXpY^clmN\{JSS]}P]OzNnQbk`aQdRmERuAQ}J]GtIm`nsoSpX_\UxUJiBeX6f{4OKKoVXm1@j=,B(:)MH!Bj6@b3[h/WwJR_?Zs;NfMgn:FO:RE?I,aj;Mr}JgHZ?{x7PRyGSE9S)=\.LM'M%=?ÇHkTp(rFGOAVD)\J5m`?f_5aV>YR3p{D]Sms:[u3}m@wMVWw[VmZOZv5Uh*Gr/A\.Ri.Mn2U\/KM*[W.fFexKtASTYfBZuFUoMpHS|\iVYtGpGv_YWqIksn@rC{ib^BLg;Sc7b]/bz=rBMbz_i}XsVicxZMgO`w~azTң^jpmeZ]S}dSUDgHܤ`啔jW̒lۤ򺡴|Slz|flg_wbq偗vs܇윮瘘|kKkAN_EN~KMzUfOdT{gsrqˇ멣㢈qzVjwwrpVVab^z`ǁshy}t~ȋxmy_^wskSI3O";?^@"sd4fw8O_03<),/+V?+xyJaMG8`rIo3Fv:qFuzsbeDX~@Rj7Hg7N_6Jg9Lf8WrBlxV{zgiёrZwH}wHtP}b|[\}[sЗvrVe~ȤǬxpVgPo哰朼쇴㒼존뢓՛yx[jGu'>9'AC*S`9cfBmjVeuUmp]^T}QYpMtMbKTxKHpFBd9@^0=^9Jf=Hi@MfBKnPYtQZn8KT3VfF^uLWlA\hFbhFueIzLyvEyxJvzObb|x\hvFSb2JY-EO4YUA]_LaaF[^@P`5E]3ThCi~Kg^yq`]Ol:4O.4=(58#),(EHAkoO^uI5[CM]FNeJJ_G`iEXjCMgBF`MFv[NRAq<7Y2@Q;R^DOcLP\F`fMaiBR]U5@Y;B_IZndof-$!-&!1 -+=<=S0&IC(>4"?3!6.%. &5"$6)-<,'=(+2$-738!0>%L:)AF0.98D.-.<%77$8<)L@!?Z//G4,8+<4'1:!48%-/9-AF& B*R6"J[.=s5XV<\d?^v`ay[YwJQsJPu>Rd8Jg7Yf5 KE!E`5Jkq=pd>Qj?J4Ki)Ni)Fy2Un6Rk,NZ3RN2jk=`qIa{O}M_PaKdy?f{AzC{OnoFnrEaQvAgXVQMqERY.f^5\s@Y4LtOl*fi)Iose]ȳj\uz`7mqAKfZhdzoH~KQo3^}9wN}atdiET,tDl}ywhomlr_`\huia|mpdwlUlJSeue{hkrϴxc|ΌыwhbYwۖjoNLuEWUEYlJvTlnavnwݎ~eIoykfeag~}[~ҍvjVsUwσUM0J+6L)/D!0:98$3Q2*M,*G";@,LH5`U6YN/OO&Ti0bf=pSc]f\kXYHG]/KY4edƑ}pEUh3\m6Nn6Ng5Sa9Fg8NeIfjvZROrBpoH\U\edc^~Q\cɹuciʫܬ̨onsskMz^Oj}w~ۈ뜻֋plmNj?b@Z>Vz5Ta5}u\}zyRkx8opG\SLLn>Ij@Qg7Wn6Y5kCZyzjagWhT_h5KQ(3>&+4%5"*>/:J:FPMNoZQ}OnVz^kvsuWlyKppPdwMfUVNCq>If@IdV,>[4P^:K^6FR9Y]BOeG[b\$',$*2!9.%??&3K05L8-J;"B65..7&0@)-E./B78/$+ /1C?<7%V3&4@*2)1;("9<%8=$85"J9 3d23E5,;(99!7B"/=!*28*7=$E:#FR-BI70;+67$E@ =Z&X17/+K>D[/;g;?Z4R_,ti-l9`~JwRL[W9Gd@9G;9A.;>&0J.,-&,45B"JA#[G'ay:\S^[SrJCa>DF3L\-Dr?MX6t_1{n7SrpKu?g[ZR=lv2fL`S[Y^NcW]\QcgsKVL`aBXv@^Qehak\wqdxfesRi_vUHlLLe@O^1Sb,Ml[O*k5U^DjAIT,RC(=L)AX4PZ4XzHXzN\}IeZL^\|Zm]lgZddZqS[{~R~qvEZElX4d}:EgFi?~\grb]XPQAZ}CfK_}G[xC}F[aqvImI_UHk;QO3fd>ak@ǐPlOYIp*aq-v>řdܬpuPkW[itCpi6]6qgDrVmH˅\veFeLMk~rHdEO5rT-[:_8o|m_j\~ptk\dKlBiYt {h}Npe[xUq|φner䘴炙ߗzlje\eXXQ]ZuSqĘʙρzfVjc1VrV|Yy~읽ufyh^vcՐ_WHX7A^14P"(@%=^AXWGV:DOh:XdIZ]@qTBQFau\Xkrlqˍrn]a;lFIkKt՗~pJb@CY8NZ1`k?IjFGrGJsMjwUgekkFi]EXaRmwmҧΉ~t}׃|en̰|ңlkd[j_KT_>W=oGBN\r̀ۏᓛxZXDh,,""$$)*%%)/"3-6B;17+0.8'1/7+-#"-)*3+.#&- &!+"-,&:,/ '"%BA-U-D%)$!!,.+&5"%# #/1 >D->>'E; D. !  "<58|PgQ3-+ #%(5.974N/GC(TP.'54+    +   % )   ""#!")")$0 +<4>EPZ^I`cBQQ:5902-3A=-84/"3.2.;A/-E52>3/=).;".203#=5&>373>67;*RG6e[<[\'23#++)#&111BKALR8?C%ED#LH+FD:FB4RA0NB+x[4f1_R%:<0DA>OB5I8)I>=;?>1929DO:Py8Ge9BD9KC;P@.7:827VA.CI-Lc:\vIbLfPhfofSVQDLjJgn:I\1^R.WQ+A?3AF1BG>MXQXbTYm[ms_biJYR6MT3NJ;(4;-G1GR7QfDCkK6R@/GC*8o7`Tn||wweTly_z~ctĪxyqlK_SI``QjeLn`]xSbsFXuQoƠhx띧opjzpX\JEHF4DSGK^MIC?G;234+56&    + +  +    +       +   +     +  &% +    +       #' ,6"?&A/%40'&)+)#!#      & !)#   +   + + +      +    + +         &" $%)$&-"+(#' $ %!  +  +  +   23:J/C949<'A'     +  +    &&#&$#4/#,7  +    7+.5%#-( (&$! %'"&15287/=;;?,9A89>4PV2EWD^#BO(@D-BV!/H7D+4 1'5*'(&2+55#,9"8H0$?!+6*9&.*6*.!(13#E !>(974/A2@@?+5(<#-:!(7"%0 &$"(2#40"0/C(&$/%%/&!   + "&")%*!,(1(8!1!'A!(D#,(0/SlB-+-1)*7(04.)-*4%1:)GO.YO+[K1KO1YY6mjP^T>@9#/*#--)(0--4,G44N2AQ2:N?Yp7IZLCgh[cV};IL57>INFAB3;;/03*5:>D/CCMe7OR:8F>BN;VaJ}pG{Vj_mWdjBfi?\p1>B0.6;7O7GZ;DI=:D.5=02DDBM>CP>@3:A0 +   + +  +   +   +    +    +   +   +   + + +               +  !&'"!&"/8#F(F4!28,)-*+!( "  %$$ ##    + + + + +   +         + +    +  +  #% $"           +   + + # +! +  <'B:68+=*,)&*/)4&" ! # # +   ! 8&H<@?'8%)      +  (3=83I&'4    ! "&/'0*! #!#!,%<*@&=$:9$GO):RD,IU7(7&-*%<8&A>?IIKFPDDVB4G6:1!7/+,$ ()4<N%9M*:2&.2.78:)+&$#)* 2; 5K(D'* '"(,"&/B3!   +   !!%'$/$+##) %,/.T,>IWV*FF$F@56+8( ()&BP(XG(O)7^b; Y3 G) S& L5 FO:S+[>( -*B@BE?G0/+*2/;:Cc#><]>I9KL;ZfISf-?;+9:#8B*@I-nG&CA ./&/0"11+44/2D>L^=nm4ej4M\IH`JNkJ9D,*8$%.(,!02+4>7/86.5)!(2&,>(0>38H^cKVp__jN]r]_bXRQC[t=Rn'\Z/BC=9R:^e1XgBQdEQb^fz~|l^q`PcSJWEHW=DP=EMBDR=?F42>3394/=7.72*;7*72     +  +                   +  + +    + +  +         ,  +    &$ " !*"/%=&I-!M5*7>0#+0' # %""$4" 5)$) !    +   +       +     + +   +  +        +#' !  "  "    + 9,36,48)-''#%"*(#+ + ' 3195B.;P9%<,$)" +   " + :( A=6R+);9"/))('     #'')359B=,3-.++."8((4/"32$48?;&796,1273,NC1FL5672>50,.43$/@,553PF6Mk18Q,:F)9Q)4BD:$33"&%$&/$2.1)%2,#*.(;+$18 188(2%-$,A(474<&7@/86&-+6*(0,)'.&/#03 ,%   +   + + +   +  "!%#! "'**<2*=@2V7$7,%0%0,5F"<%)FFQ4,g'CO n%Tp-'3*"-   .<EV)P[)MP#g[SU5B(2B(-7(/<0<=?5AF}}E;v=IUEJ_KBY-DX2JX:HeXOg_am]SqbNswkƷqsfԁ͌YuMfIJpOTnMM}NNoDMiB]fD^k\xݵmSh[HUPMYJMZ?GS:DQ?CP3?G25?11<6,75)41)/2/02 + + + + + +    + +     +  +  + +   +   +     +    + + + + ! , #  +  +  $3=%D+ I7-9:2((3%  +!&!-#14'9"#*               +     + +  +      +      &+&,%(   +    ,%011657)(4$!  -?&23'&*.#9*(51#*:@'=&  +    +  2 84(A#6.&B$2+$%%!    +!&.0">K&EQ*8FJ? =@I<BI9G=E&+K.4?+=P#3T?K-5K97L+>B-?N!8? ?B4A>*=Y5ENT0AA-6./8)&'* 2*  !'#!!") $747+%%'%*'&0/'."+2' '5*"6'"% !&'  '-% +) "  + + +  + + "!"$( "#+-$,5#:&&% !#  $$D?$@'?#U^XQ=aMTlHT_5OF/nr;_oQh|?-HN/WC,XC1AI37I6PLA]USHNFZ[9O`B\h6F? ))!-.%"+#"')45,-8(-.!)5"'#!&(*,0241;9086:.05+003,.B/3D9:MH9995>IHivekb64<2075/;5B9;@88*3HISV\tLfNSh@\]:QXAUdDOdbXfCMyL]uMnyBixGgJs]gkkfri]Po`xFQ7.3=/.1'+-,+1).2, +    + + +   +  +           +        +        + +     +     (9?'I."U6.7>7).." "  ("!!#%'$!',"&&!$         + + +                     $,$        + + +  + +-$+&++(3*,1#$    +% +&F(@(!/>5^F MO+*8&" # +    /((1!&!  +   ($:-&3836B,7S-4W,9M)7R*6N*6M)9Q48@*1<*83)7@1*8-9//5I80O;+G*7B$5E'5B9(C5#2(!!"!&+/R08)*+()($1! !%1$!   +    +   !!*,  + + "+C, + +  !-@::O[IemE:J6EN8K[4Le3RS67?48-TiDPf>Ke8LmCJoPWzXaGwQl}mqZ~kGlgFl`:^dDeGHiDYjHfu=Vc1Xf+Pf4F],FS1LQ5MX2Ef8@_9;Q@EZN=GM9K;FVTK_OpSyvQszMjrMWpGUhQeIlr6Jc6EY>MdA/+1 " + 633$2.'4,"(-& * 07*22,1%( 7K=/_T?P\3!* +  +  +   +    +,## ! )"      +  %,#6;:#GE&4<*45*)0*(**#$'06.0&"(#)40 H:!=E>F11=,(8*&00.4+.:),4/(&$)3/<*A $# %-2*GQ$*9%*!!/ $"  ""/1)   # . %- +# 7* 2+ $ +      +  + +  + #$  $  &4 ' + 9% A1 (! -P/5M  +   + + +)(1*"),GQIejP\V1/;03F:>E@6J>8M?=FIBTGBT;8WD.N2&;!1.&.-;L6L@9D(1M6:<cJaw+6O*4*0;)B>0BB5B@1GE-<@A>I5BY_DDXUUpNPb>^g4O^.Ie2^h8ch8Y`8HO9;I96@>0=D-GDBV7ik=LcHWdI=SZFia^mWWcJR`YLcRRlB3R5C[>I`GIgNNXKY^]YkzkmppJYI?L=7D?5C;4@8=C77*;<%%4%3*23!$3 &!!"#%" ! #     +    +   +   +       + + +      +      +  +  +  + + +  0*!I(='*3 $""1+-6&-,&--$62#6(-.+(-/1?8K%:'8=N0Ij@hkH>a6 +  +    6 @0 /G/8(*-)/$      !)0,/%:"+%"2.7*-.+.5#%3+, /3&.<,.J5&9>*>,5#/ +$02#A:"@9/.'$.*"5 '@.(2#18=NT7>G$!"$!",.7/!" +      !&"027E .J=FC5+( $ 0&@(I26$ -''"  +  ")$+0 ('!(#(.>:M!F\#=G144;0S'3KRrWo G& F-mL N T, +gDD3I.2,A  B?LV\]H[@8D'4:-VR-BI:3;5.=09N?0:8;@;JC:W?&=:%GD(YT5n2IH*J>.IG?SKMY_=L`=]eILXE7CV0Ac6D;??2<>-;8./3*,*%,0%13+:@-4:4/!**"$) )('-"59,7912<4099(;;.<+;L9D\R]F}=t5ed>F?c;@i-BD37,,1,)016F:FD6AJ6CG3LS8_m1O`5eA[qHe|^]ADY67O1>E7?\D]L\BYKLqUemJOeLEOIEb6EZ1D\.>Q*;Q.?O9APFI]WO]HH\>;[A=]DToXr~\nL^X83E3im9]:(.C(8O6[KNkNck?M`:BTImP{Ol{PYlWI]@>C/=B>;HDAMFIKEcRRYim{t{ZiTGNE>F904?26D5,5:+-:4/97-2.*.'$+$      + + +       + + +          +  + +  + +  +      +  + & (!'%" +  +     '3>&H4(>=5%:>"!-!1(9*43#!'$!"& $+#"$$#    + + +           +  +     +  +  +  + + + + +  + + +   + +  =/!+,2,4#8 $0!3()-++'""@30D!3<# 4'##G',CB<827); \%h=f#1* +: 92 +67 +6!I-H) =: 4*'6!>!*-5!(,")$#2 -""5'  !"#    ($!+""!!"%#!$!-(#-<"#7&18 ,7 78&C=*<82'/***#!!&.) ' &3)AK.6F%`E"K?633,<3%9O%=4%5EG*6*%;#> 2\%AT9O,C-%U@.a<5[)c|0a%:_(6'#' "$.5?BSNORLFW2Wb&AQ1-Q4+=.,05*55 /),40>]5\_5Sf-N]4|sK{KNY3?T06M:XcBqzOVgJHXI=Q6CF38@;A;6:E8>E1<=,5=(59#13*:7,3E2VO.9>%-5))$,+!10 72/76*-7..50(-200(XL9JFN9>b7Vt:XZ\Fg~:jp>RP315'+5$22/07B7@I6B>5G61?5?T=;Y3]yHTpZDaYZ,>_$AP&9J,=O=TfH\lDL\G?W87SKE]itg]qAFAiAXaCUA738D:>JX\TcnIM\>PeaiáUZh{JFTBUa6]Y9IR6EK?S\H\fMTgoepavTefGULLR@OG78?306+1=06B55C9;G0!&J4>:(&2* & %.,)#3%&+;#?DDZ67]8S%# (;'" 67-UA=K?FF >K*(C=+.'(/;* & )    +     ! %" !&    %= @ )  +  + +  !     +      & !&" %(74)'<%-4(7"37?5:2H,!<5F=*E`6\1    ,#-,/.7I8FIGAF79@,:6.3;*.<,&6(2=0@F)@?&10!/3#//*;7.:6,03()/'/2!03)190CW8EO<>9G7CN?PZ8[V1FM6^4>I+55%14*/812:A07:/80*5-+7//7-.F>AeFjNZh=WbAXcRXjKT[GRa;?R1;V,BY)N](H],LU.@P=NcLVrEQf/KY.CZ9DZeZzsSQCPU96D238,47/;7,902:/43)*/$%'%"'"' + + + + + + +  +   +       + +  + +     + + +  +    + *&$    + + + + +      , : Y,I<%3:4.*3$#%!%!"( &%)"' "&+++(    " !      +     + +  +  +    +  +  +  +   '$@,LT$?Q0 7,!'O2#GH=C!$=!"6>!7R#"LH# &62*G%*)/)2&/P(9R1?M70H;+81/+ 1/#,,44<9"  )!"*(+&4)!" +  .&& (%%!&1,8HGH)BK/4HC=4#!'""%'.6-N)'G",*"6;-R1&G."<%%81"D2/64-3/;B.5L5-9":!0"#"-30>@gGF\.G8 ")     +  +     + + + +       + + +         &"'' "+. "" 2)%(?0G+\I- ),&!)& K>T?4<.?&275%1A+44$4G*634:%" #(/'%&1$(/I(6S0*:7;IJ]JRqZ\eCUE15/&665@9E36I5G?(AB/B;453*2206=/:A?G@5I54H1391113//$*/3.89;I34>';7*.5-274=@0:?(,3%35'=;'27*JE4JR2JQ-W\-Ra=[fbW?KK:AM*?D0@H27>3lM/rD+7/'1:*6:&7920@65MKAfIlU`|qrRgS`nIkp?n|OlzPoq>Ww4mz2W^+Q[/\t.Pn6Lf;RlAMdOGmBGb6MU1G^:JduBqGP]>OK:AkYV{U|W:GBHCK^C_INnQqzsTY547>385-936P95Y@:XGKjdQӝnW`WKZMOVCGOK5GC2D<4G17>0:?01=3*32+43/40/6,36-0/#*+&')(   +  + + +  +   + +     + +    +  +  +   + +  +  (&!$' ! + + +   + +  " # 4=%W/!KD+<=;&1- $# # %!!&$-  !"    +    +              +     +     +         +&9)C/<;/5A@*34.!,/L(!IWI@UC272!-6$,;2&"0!))+/%* !$ >92GB5;2!8*<098>D8HI8)0)*'!'. ,%*-,!"!!#!"!"  %  ! "!%#?,-0.;25I=01E<-F9.=2$(* !   ! ")#7""*%#6!$1(&. /%$/#..0+9**3%"*%!!*!6:=J7!      +   +   + + +   +   +      !" %(#!$ /14& !83K7?3'0$.,*)!'%,-$3,&5 ##+@:026/$B53oM'QR7PKR:EKFPK[^3e\j[)Lh,4=229<742-+518-)/)13-.77=A<)65(56(-+/8-0<$:?"KD7@B<724;%1<*.;0+8,MX7Y9Tp6Ld2e5xC]~JW|?Hj8@R57H51C74ECFsXf:l~)JO.?G.TY&CH&7F49PNDmWN{v4\=egKniTpdRkZmrenei}BQpLb?CXXP|Zx^VD:^39DN^CfrpfburejeMCTEFP<>M89A?-;7+55080,<,-6),/,+2),44/5127+./)$.!)- &.# + +   + +  +  + +   + + + + +  + +                 .,#%$ + +  +  +   + ' '  + # +4 B)F/$E>;>8C&05("#  '"!&$$  $ &!!'"                +      +   +  + + + +      +     + #2F:!:P20E: A,!,+!%*17S>WS%J>6*1& -' '&+"$(:%:-$D+):"&4(*3&644B&49)" !$*%&#'($& "$  $   !"#,)<>TE#IJ&5AW6*6F1*83-8,49!*+!-%% %'(/"!!% "(!#!( &&%(&0   +      +  +  +          +     +  %   $*;7;2-)/3+&+ 'D0 !!$#! #%$,)%3 0@%L[4WtCEd=a=HL6MP?esFQoH@\FQXFGG.14+69'04(+++(+)'/ 0-,:755)66&(-()*1&2/,28.=9,B5534+40.4+1<-555-:3-60&23'/*/.)2:2Ab2Mb5Vg6R}=ZF2WR4MM+@B,=;(64#1106F.F=Y9QU=K8@VCH_;5P'3I$:P1RWG?O=0GE4DP>VVQb^SmngiRjQ|QdT]HAL=6G/:I5]iLn~_`h6Oq2C:/=9B\R11E'-@(3?%0?):E/;IF>Or~t\vjmS`Zi}ey[hsLX^COZ@=G52=,-91/4./53,5-*4):0)-1(/4-)31'0.&-( &%",!)*   +    + +       +     +   +     +   + +     2 +1''#   + +    +  +$ &" + ++ ; R'J6#G:2>=7/3.(%"##  ')##'$,! '!."                     + & ,        +  +  + +  +  +  !7&14;A(49/'!("&. &=(34%"0( &  + & "6#!*&%*,#% ( '.2<*9!&"(+8"5%1%-2107/3'"" ( +"$## #!**1,.+3?8(=Y5=]25T+,M+9Z4J &"!&! $ !* #)             + + +  +   + + +  + + +             + ($" "(G9 ,# !"$(%%%'&"(:+;Q'A`/AZ1:k?MA^bLLTe>NO5GDI]@W^7YU(A;06;(07&"&%#("&(49/?E,AN,&1'+'&0.';=1N>603766-30*43,/813G1>D++0)38(iR+eE,H>+eS0fh5GZ;1->C1PUHeRap\OjRNmU^gnud{Mz0?A:Sagth]}Xq?s)(B&1D6_pITtKLnDJP3JI/DO4PU=IYFY]dWbkfrkxv^_cADA18=.4=(24,03)=6&:>/:D>_Ukizdojejsp_lkVlS[eFTa?AP26=)58),9-:9+55(C6)`>*X3(H5+27,,-)'+%')""%"$+!        +!    +   +  +     +        6 +0&*) "   + +  + + +   + ! ' / ?"I,?6*B8488>)./ %(  2*#"#)&!%.")&*()!%!'(  + + +                $ !!I! }K)U1      + +    +    5@=-73%/*+%I:*> !'!$'%,9/7)     $   '&%.$*###& -(&3%"! ,&,-!&'*!*$/7%'9%15 ,& 7+-9*2&/%'&("$2 %1 )!")!!4#)=&%KM"@K&39",:.#<$ 5$"   &  +   !    +         + +   + + +   +  +  + + + + +          +   +      &"4)3)#*  $.!  !""&*,)((&-,1C7:7:<68:H9HLSXIRJFH;3==?ESESOMWC?G2>B/CD$8/4/2/!/(+47.7B5,6$C>&O<&::3;E,>D%GE-BB&36-,33.2;=A*78%6?:FiLa9nv@hzJF`E.697E0=C3*-&H<'D71H=0BI2IR,Y`+LY'>C-@K450*25"%+#!   9.-""!)( " "))(!,-2,5&/' %         !(W- 5T&%#'$"  +  +    +"S MM=B3B..+&53DOW`',[!(D#&"/0/&?(*6521   # 7&! #) *5%.-(!2($(.)!&*393/6*/51"'1#18=<2B);$'6'4-'+ * **!&!%%(* "  &+3 '' !'2;N);& !  2(0'&#  #   +   +      +    +  +  +     + + +  + + + + + + + + +    + +       " &  !) &&+*'"190+%$'-1'T;@+ -        '*$/-74<.52&5A1AD/GE3B?;LDJCBFCG78A,/>6/@K1LE6K324<-<38=)RI)CE)LG+4A(<>#13-9B=-C8/K:=G>0?4,@7/<4,8*(1(-82?L4Wn*Im9If^OYoHi]JY[DYC8A+=A,6E#CH*hi5`~1p7R\HBS@=WAGX0.>/-4-,9*!.+&910E,;@(.3(&3(5>$0;&B<.=A1AM8AXEH]@LL06CB\zOjW|ZWwEHBK56986!:8)DF2AO68HF7Kc6\^8Iv&/20'$6/,6$-!* !! &"&)')-+5 )3 -+$$ ! +     32*66((,    +   "*%689QJ;;MB@8812+,(-.:7%OL8S7,*>J9K4BAAMaW#,!"! + ,%2 "/(&'#%)%(1-$(-%5".&!&0 '$%')*!4#%211"-7%6.&2F99#)6(,-.,?+9,6,8'11$ "+++'" "7&2""&  @$!5;%,.!-2$ &           +  +   +  +  + +  + +  + + + +     +        " "#$ "*)-4!!+5;&5^8Q3J>55=+"$ 2-:<#5<,DT$D=,97>81?:>:CE?HF@@G89@:.>-,40-76)4@(37983==2BC=T\CUY.*9/5C1=02)/3)-/)+!,+*586BH)JB&;8,&1//925B5;C91A<;F58A.?E.<@0S=>_:B`?=0-+()&'2'+1)'-**.$).'   +   +   + +  +   + +         ! " #39%>*)93*)/,#!!!5 ,2!1.'22#..(#*'!$%!,/$0,#.4!,7%(-(#.", %6,2+            "!%*" &()   +;LL:IZBBL61T/I",$8)1C,:D 5*&-0&/#%/ 1C%A+(""#$4(71 +5"1%- $"!!*.13!7-57*2#.5*"66': :%$3$"449/!4+)/"(,1%-+"% 4,*62+9525&#! '"%6!)%"*&$0*     +     +    +  +      +  +  +     +    +   + +    $ (#!"'/8&?/ (0."   (),+,-(-&*,&0.#G37H5=KGgf0BP-RV3[f;U[9ET>2@9.7-.6,21'"1&#/$ *+.;69K47CE=SW-IF&=B/M>8R8U_2SU1;C,?N%5=-8=$6?)7B0<>*KJ#D?'7:+61.+,20674:;(40$/503---,'-'06-?<78;BN>6=SG>^W-SI2R14D031'25(:+T;8>DN->;*9&+..-133:I<=8*7=67:(2:429/>NL^eRiZ@deHqkGocUnB\pFUHqJaGi9U_/AT3Wc;Sa.G[,EV5GY1@P,>Y@BR]6Kb8Ua@YW5LU1F=nhiFY2)=7:SKNgZNhbXvVhvTjt_kxd{z^QoZQcMFZJJP67<<+99+48+;78984996?/7F2+$:1',-*) ! ''%&& *!% $''-'"0$"*%,(!$&!""-,#(-%#(         + + +       +    (($  + - + + ("2I028E*4>".=1;")&)&#+.I,A.,0"3/+0"6!0/!5+%/0$'!1 $)$%  !%"' '  !!&)/$8%0!"*-&*5((''!("*% !#!  '3$C1<77#/('61$/E$   +    ++ "$!$"     +    +   +   +  + +   +   + + + +      + +   + +     + +  $)0 !*# "  !#$ -#)<""-'$$-!8#,5#<=:PTQ`9HSJLWUX[HBLA9=384(16)=9+33&6."72"/.5+1:4:D2;K/8;+,0.-='/D(06'69)FN-4;%,4'4='@A 31"-4++7.5S<09@4GI<#/A'414C+BH.GR0JQ7Re7s|?Azi?2<>%.)&2%;<-KO9AH1103)/,(/,.*,,,E/?k?Ld6ACIVPM`SK\GKXC4CD2XOEuU9eSSrJDRQD`Phi6&:D*8?:@0/4>EO=h>|Ea8B044'99;LZM>a`LZkHac`pk\lho]]^V>TG>IA@N:9=5,30%2-58-9<64<33<3+=3)>8>GH+%>(%-$6E$39$#! !   *$  :'&&($$'9)&2)4 )*13  +       +    +   +  +   +  + + + + +   +    +  +   +  "          !!,.!))!&-"+&/*  "(')" 1''!"%'(.*1C6OB5B@8KK;SO?K<9@/JH*E?419144%99".8',6)0/+.448517=)DI"@B#--/(%H,-A27::A=,7?-J>F[82LJ3YH/RB>DGLMfq29,*3111',-)351>G@@UD>OQ6WH7O27@-98,5))%),AC2LK4B>9GGZFSw;D]8CN:7/;5+G6/C9;=AK:C[8BeEUiOcuJeU|Xt`TdCJNB=E:6C84=.*3($/.4?4;?039+39-032/1#16+)'!-9**! 0-/#A-8A/&99$&+ , $(#-#&! !     4S,8X2N24@.HC!)+$       +  E+)'$)!  +%',)*>*J?"4C07,2"/ +1)(*% ""$!        +   +    +    +     +     1 +%,           &$"&.$&!% ?) (("&!-&-&.07;<+,;:$AD,I9A8<7)47@4G7>N=8K2;B,>C5A?138*14*8;89-EA0E5*=73MH38J-(2=#6D&02%,;!*?,79/1''+#('*&,,+6123(<5';7,EG14>'05(2F@B2C>%=2'C92IR>af?eZ<^WQkknTiLUV8<.78+*4J-G>,00#+8',+"0*61!2*&#-*'1/$-51<6@G><>K1?Z?SCQV2YM)?;*5951<;3>81@H@E<'8'2;/9<@4C5;P-_W+dj(l4U@K2BLFTFXPH>C9=I=XdFZELY;AP4:@4/5)(,'&122>41>15;+1;,*4)2:7wMCuF50$"& '$##" !""))4!    +  + +  +  + + + +        + +  +     + + 5 +2&(  + +  +   !#/ 1%7% ?+!8/.3/-.%+$$ #*$0#(#$(%#,)&-50"#%!(,!*)- .,''2"/+#"!   +  +                         !!*'.%+8"1<1/+4*-#)) E?@*2!8,;=:;?H8V,75/350I2?%<.2#03;, %,,*%//*(5#>0) !' "#        !    " _9 ^n(7GA,-5&*1$!!   ! @1$1+%$   &$4/#K4-@4=@*GL%:5#83#++.2!2&&*-!      +  '?'   +  * +    +  *!9U#-   +  +       +  "#%# T460+-+"# "!!,('#% ! &1,,?)9<-735O1Ad5+A+'2/)B2+580>678A9@??B=HH5?J61?(1:+;L44>8$;.)/"'+.#)!&$'&!,.1<(>721$;;'@7-:A,JJ,/?00:7%3*-:->E529HJXQNlCXe6ek@[t4bx5xxBpYcNiWPTQvB_n=;L/Q^(1D8'7N&/%&2$,8&'4-/;*58 */#15#(2)#/1(<>,D6BHD8LIVoD8/gY2*=.-62,963F.,4$4>%BK=@D>4.6-5:1)46*3>,@A8P@@LKMTOAHK>S=?P6GK/=='#,'!)1)05*32,5.,7&+4(4-;DH@E787?*0!(#!' ##"('+.&3+)'(F%:M*6.%') '4),3(-4->22=;AB:5BE-=A380:75?=<307*0L3GD6F9:A0(-6-,7+5B@J7+.8('&* 54,,*+,-!71).26>@+PY,U_/@K/7M65J8/7*)/.!%-")5+:1(:0OcEbApEQ^WZgZm^X}OLwKDfNIlI3I8+C0/H'1=)-6%)3#*8$#4!,0//2&'-!.5+/5&/(69/$571D08N@JdlnsU9<>*$0,,40)8219);E/N]+@O+45C6@nWllpjvFBLKKWk~uMlM>L='46-3AA=EC4I4BF'40%&*')/+,3)+3-+3+38+F7G?:L91,%&.",9',<'      + + + +    + + +  + +  +        + + +43'+- " +  +  + +  +"',9#O-W@%:@-20-3(!')! !# %#&"*("0(3A#"5(!$ $ %''"!. &$(3/') #'(!( #%2"2*,!$!"24*+*(        +             +     !&*!&#2)1$E(!>-@=EL'HF28A)D13) /279 :.(3@C)6C&673+600&! **"CG-9B4%.-# *%$9/5"#% *"(#.+%( ! ""  $%MI!PQ>-87"*$"/)" !      +'D5/33- "! 1"//:+5 6&+7+?@+8.(/'(5 "-91-+%'"'),*-  +  + + :+/$)J6XCB`=QCHPVY) +( KA IH$D_LPNJ>Z5$B3!AC6b9'2(*&2  ! + + +      +  +  " : ;L.8%"+,16.2G'/8''),3/1:2/,*,"(!%%"',' $&'-00&?36;A8&/2'(-.)/3>B<2<6A<4=<.8?36:7?=-5;-6>=5>=5=-;:!.*"$.44A5>H!*)/3$:S%A_"?:#<7')>(4;,BR:]lMOyIYe;K[8T`9DR'D<%?061&?K3>T3/ELAbC>mHUzRZ@p@Rqe]vjNm_>Y:?Y(@[2>\52N(5?*7C 0@#,B"37#85 ;3"/-((/$94$+5*A@%))$#/(!*-#372@8>RpSjiZt^QL'5/%45+<:-291QX%,3+2418G1KL7AO8HQ5EF6?B76?8#;*!  +   +      +   +    +  + + #   -/0&+'&"E,@_#:F2IE*6U,8="*-1.,%/8!)-/#38=B /E*1B!1A&0E)6!#,,12(7#',<)%=C"5A109.B,8,(, 21180>+H*;:/!,)1)!*/ # &&54EI!8.1!   $  "A69@65%%  $#03)2".%7*>&*F#26#()"(,!-, /<(( +, 4" )2& 2*?1):%@0A_AAS<-J/7?*Sh9_s'XlE`+;L6I]DDe?)N:'&'+(>4:s\"5! / #. !! %"(%  + +   + "0!)7='9D$DG)7<#91'% !'+(-*&)),&+)4)+?-)3.+;.%0) $.*,7D:2F5=E7-A525;094,44'-1&3/(51&;97J11;)0'&,1;3A1-2%#!!%)6,@Y3Yf+9<.sJLe@JjQSSSNxkNSlIifwpEulLjbU\g{b>YR0&.)'-4-A+1:/8/223=>7:;.64+:?2GF;KR>EQ8>V;FW8Y]5WR9AD9>E(19"/8,4;,68'-1+@0+D/1@+$% "'5=$?N#     +      +   + + +    + +  + + + +      + +    +  + + +  +  0 3&*)%      $ +5$H$L4*<983-7#' !&($%#") +(#'%$%#+$%#*$*$''* "#(#'-&'&*$%#!#!$ "&#0-)#,/*+-./&,6)'63!($$   +        +   0 :6/9/" -+:77& $#166HF6=<9G6-I= ,3" $, 5#-1/.7&B!$,*(-*'0-04,4<&$7%)/(- ) +<59@N<T^&/Y?L1F/8#3N%7>+A/9 ":0&'   %!#" !+ (5#+  % "%*&  :36907)&    !+(7' $*2%-*215%&*-(0;:2@,;!8"#6$$<)(3#6/,2?'.:(29,-3.2:3)8_9Qw[t~hD^C-D=>TRV`m~Qt`{TJX?P\9GP=AMHDUJQ_@JQ5=E.@D,;@09;*9<)26,;/+6++9*#7 $##""$(8.BJ)     +  + +       + +   +   +     +      + + +  -4$)( +(    +  + + *1B#N-\:)HC4+74 (   " !!(!"0&""$" %$&&!"'#$ *"$ #(#("#*("*1"(5)--,'.!!*  "            +  (*= 1/"*$- & %"-)#'/=(!:>57(;9/4F*.?')85/5!!," %'%!*$".&2+ ("'282!/E 7C#;B)`G`{-7EDZudqTYv-MP*EP*8T.Fh.+4/"$C '7T%-9&)+)&/2)45=H3AW1=E+4L+1K,>L,:C%:<$4;$&1()>.+@.'62-5<0F>4E3(0 #$61.0663C;/H22F1.3)?>)UH'JE$,05+>f9XZkMPD .G';b3]l=V\I`{=OL6B1792B855CAB[F:WRatofwwXk\8B,57(0/"-1$/,/'24%3=3:8542)65CG@AI@?RBC]8QZ:KP*      + +  +  +  +     +  +        + +3 8%*('  +  +  +2=$H)P5"[?-EI:067%'*!" $ #"!!!!*#+&%('##!#'"+./-#*$$! "!#&"('#!# # "$!$%%()!&*#"'"  ( * +     +     +  + %'+!""%#!&!(0$" !&57,&N*--'6*-1-)'9!-7#/=5 1+.'>0(+'%/&86)8G'1C(+8##33-938N"4I>#91#5$- "0 "-$$$  %%.,06#'   !99##=%-/,2&/!200;-/0/,*%%4$-+ !"        %)+%&(   ?01'&)     +  +      ,!5# )0(%'0(A!  ),5CAc6?04*)$$##%-2M9-'.(2&"-1"$/+ /'(>'!6R):,0%$ #-*      +     +  + + +    +"*!%"#-'#33174'%$#!",-77>O:;=,.3/#4.!) !+--'"*:/$()))(1"'/(%)0'7+7A+=E%-1 *+")%,)5>,#0,$).&,>&.622/(+)*,)-&**;,(4/1C/Dd:T^k=am3UMD_6=V8'/*!//"6 )2.3+3*$>)":'1A*8 2<37C4(B;-F#0C#0G)OU.>K-;J38E(.< ,3 (4*190)/0*29-7*&/%#/3--4.'-1(*0#20$0+KV3bm6ID>B8'.3,@/;J--9:+,134/5.143-13/917C=6F>7D@7JDAR>BS==G<4HBCW?IX9>G8=IB=TB>U>AI,C9#A/-bC*T:81"9?*7;*,: )%')+(%+/(   + + + + + + + +    +  +     +          +  /8$*,)  +%$77&=) Q/,O93IA>.9:)$*! %#"$%((., #,(')-$413-7!*3$)*#)%&(#!##$"!"  6-.!4 !!+ $&#!#  "   +           &(#(""%((!("!+?-5A#GA&8 )#$(""'#2315 3'&!(8;06E )?4560&!'#%'36!&@2+:0.2 !, $+%)."!##$&&'-*&+ 0$++-/;BD;&.%"#'34; A9 3=0=;F)17-&5'(2"%!/*-0.+&!!$!# )# +$# !" )"% ! '  9:*'(& +       )1=<"28'%&%%  #1)%(  +   2 P 1$D&0#2 =8!'L0b[NSJR ;."#/$$.#,'1($%,)4 +",(  +  +     +   + +   %"))-%46-/1,$'$!*)5'-7,6$-2"%.$%*#)% &+($$# "'.%"+!+#($% #%%2 *;&392=,('(!#)!(5$+!*/'$+%$#!(*"."#(#"($ + )/*(&%&1/*18"+!$  !!)&! ),+0517,#-'"-# 14'F4H:=?8=>::@>&C.$V.)^;-OD;/<=($+!%!/'%,7&28!!6& )%60*7E+(; %.&.*#","-0+9 /#  + $!((#>8-3'; ,0     + + +          %'"#  &#&.21B$51%9);06(<050$#,G0/H$$$.$1#$$"$'!&&/#.*'$,,#%!2$#(*(##,(!!1)%&!"# %!#& !-' %8!362/(3 .80(DB;)8\.2I+9K/$@'(/(4%,!")(81%- "')-** +     1&.'%%&   ,4"(         %(,A4)8D8A).-,64()#   % #6"41*:U8C-A8'2# $-2:.0 B0!"#+$%$ ) +   +   +  + + +    +%#%",$*238@+=4 ' !)(+-%*3&%,%1-!%3!!*1 "'(&$$#""#$)$&!#"#*)*845684)! $! !##!&%)"(-))"'/%*)!*(0. 0'%##(%"#&'2222-7-6;.=B6E;;K2BE$2?(0<',>&*:'4O/9*" ,).--(+/>=,8E$)1"" #7'.%6+(9$#."+$3##1*"/%#1'-"%&'#6=,@?-($*(**8$/:*03<7BD2HN9Vd7ao1`r8\g5L9JQ4FM2@C1GG;;<4--5*)I99MHG;L63.2-&'""'!/&$%%&"      +      +   +       + +  + +($&*,    &09&:-F/#Z5-HG=7:@))-!#')".!!1"'#+%-//6(-?% 3 +#%(%4293<#*H*#!%  +  #"(*.5(3'84"!B# &)      + + +  +      +      && *%$, !" "$&&(8()8*;85I6?,*=,..$'03<+9"6''!#" $ $#('%(/ . # &'(/)5+"'&  " #$ %' $!$#%#'&,,(85%-*''3$"&3*&N29#%=2A2" #5"50630+%('+#!$((' #2  + + " 0+(!'!).!/'.  "!          +    +   .%?2$1#%+#4 Y>'&   " +!  .." !" #-!&''"11-+'/1(0 1,1*2*  2 +' +      +  +  +  +  ##(%!(%$(#%*-*55;M-MB &#$%)%.)&/&45(63"(& %(%#*&$#$& %/ .0;6 18%)54-;-#*++#.%)%%!##&*%(!13#(, &3",E#?`!Yb19 07#7J,5K+=P,8A-;P59S6Rw-Ye(09*$- -*")7#-B7B:@!BHHS4>*5A5-8-(2!%   #)%&%$ %* %))'EF-GF(6:2-'-*%(,&-/,*445D*TS#\]4<##.0+3038,3:(/80DF-;95GPDFVL;ZRGlFQvJWvIWn-QK*45.(-,'-'+-+*31,9=3:2(0.$'&&+&&.(4604=22=7>J8@G74A<7?;8M6?0GA2A81C70?73=1C+-6+=4.B;3<504 $)$" ).44!9B <>5:$!  + +   + +      +         (8 !)"   +  *+!,"-#;(&<1&(=5(,<&(.%#$')*45*;"'=!/%  $$$9!&9$.4&$1$+( + %(*: )$/".% &$!       +                %$"%"" &!($!'5$*<21>/.<,$*$90"("&*('*'"7!!,(" '0!+'!""0!")!'"",33&# ,"34#!' "*'(#(&&"" "& #&('&,,02)*+0*# '%('!!"',(#""!7$79$1=).9<@3@0.9,(%#!'0*+02/.4--6*%("  !!(,3)%?;,FC27B-/92,-3'%$!("-2 !.+&) ''&(( ),"''("!")!)H!:4C:INCAYBB^L7fFa7FP(:C'43(49+,03277'0(!(&")"01&+/*6=02:,099AL@;I;3B46>5084(35)30*.5(/7+/2).2(-12,1C6,]A/Y94F440%*"!$%*'-8,6C):L+7I/!"$ $%  +     + +      +   25&$0%!(     ! ,1 +$!#$*! ("'/+""'&#'#$**)+"#*,'18$;"%0$)  !!!&21K#,%     "-.&'/. +       +    +      " " "%&,"','.0'29*1>-4&")&1.17L<>P'OKCBGA;ID>@<76)"#!+&*#3"1-1#1""%$;+1B # #&# $+& &$",&'!%%/*  #+-'-1&./(60(/2%+$)'!! " )&4&2.5#%6#%""!   + +  +       + +   +      +   +    +       *8 %#$(("!$&(0+#6("4B&/0!-'$=* C9&.        +  + +!/2YN&@H:821>&7I3QR0?B6,*)5*366)73''))!*()/ 1, #$--#.9,1=9:X7/C22B/6<%$2!$%+' #! %"%  #!&"%'(#")-*)#6,.##"'$%)"&'&27;>UY0NF+>7 ,0'49,P7E^8\l7:P04G.-2)70'*0%!($/0#-, )&*8#@H'\E#,#(!$ #! !!"$ ',#%1"''+-($##%"&$*%//+28(+.#(&',$&-)/'.%$!5+86)380J=GY<@R:9K?(9?(:N4JS6A/)<0)71(40.50+7-)5%#" #)*$1--5/9?,=F,BsX=W9Y<):/ /%(#+)$..*35.+5-(9+);6 $""*%$!"%!  + + + + + + + + +               +     2;&$0,&.   #'$$ ''!"&)'(-,+).#1 !!!")%,10.*.**2(0&/%( $    +  + + "',#  # $##*#,!  +                !% "     $*#(+% +'$,)* ,%"$$-)59'/0.B;#=-$52%0$)>0/C*(B-9&5!/"++%#(,'!$&!."0=*7  (*$!'+6%A%2+ "!"%**!*$%#"%'#*((!% "'(;$,'  ' +  +     +   + +     +  + +   +  +  + +  +  + +  + +   &)**2D +=!5&)('#'" $"!1 +    + %"3-3E8I.MI!Zd&Zf77[D,K/ "!-&+$1(,5+&4(!#!)$*#'''$2,$:?,683+>=55>)5)35;G3#)" !! 2*-$'#"+!#&##&'*"'!!)"%8*=`^`aXA3"-,.=:?078"(*'0*3=FJU9TC'>2(3-)5%"((")"(!()1D")/# (4*:Y!C/!(2.()!#%% $$"%!%& " #(+&(&#!)$$$)0%"# #&"" !%!)/0@1EN1>U?4D7"2+#.*"-4 -A //'.'/ */%*,!),"*%!%%!& &,!,/-;19E6>H:@K9;?-97*42+08(02'),'*-$,*% ' $#!(/.7YH\hdhB^G.<5!2; ?8=<'7<+38*',+ &%$.*$)!$ ("''$# #!!                           +   39*"3-%/     + '*"+69%3%'%!  !&""!'! ('&&&' 7&&?'0!!3 #'(+&$ + +       #'%"                 '  !)"'%%##&"'"&"&,)*(1%'&$*(- *)'/-#)$+"*)",2,6+8+.0*#)('B-".G*3,,-     &(" '(!*'#6'!'%!#   L6"&6&# +!! + +   + +  + + + +  +    + +    + +  +         + + + + +  + +   #.) +! $-$'+4/!     + +   #),/*;A3HN2/A-&5.&-**3%'.$%(&!%! ! !& %& )$&+$;5"5.(# %/!.43A3?E8@I7=E0>A,8=,69+/1%'-)).(**%$ !(%*A>F\tiscJYF0.+*&-'/=):F,7<+09&&(#!"( $#!% !!      +         +      +         48,"3.#0    +"'/ (#:8)G+13++$#!"'!*)/.302=#@$"1)(.'0 ,  )$!2#   !%#%$            +    +           -)*#).'.$&#"!  $/%#1,52    !$  ,,*6694=49"$# /%-(/;0*0#(()$ 7! &5 $  +# / +,! &!-% +/#("  " " " !)$/02 -7,%!"%," !+-".$   C34EC2*,!*     +    +  +   +   +   + +      +    + +     +   +   + +     +     + !!#!$#02$579 " #% $$  + + !!-3)5B:L>@=:5:,03+/:,7J1-2##&$-(+0'(/+369-2=26C:H\D=SDAF+34007)''% +.0/(,,%('%,*&.#(' $ %#$!# ,D&8.-'96*G/VJ2/WZ+Yt3Fd64C#&+$0$$5%/@-695%.&-%(-#, #)!$+$*#!"#!#'*'1',8.25(59(EF*>C1DN+WO,BI=]i@nx3XU3FNB@P3(,) ))$-)*1&.4"50#).0/2,'$"##+/26@.9:'0)1)#(+4)37;C;AG:9L9BP7CE-)2,).'$(%%%&$%"'(($(!"4(=I7[VSgaUH]E,?-"''%(*)*1*-6(18(.8"!$ "#   +   + +   +     +    +   +   +      + + !::7$60'0  + #' 0"7 @#629,$//((%)  + !#(%-,)/ !4&-$"3&#-+/"3%  # !'",%   2&-7".!     +          + +   +  + + +   +  + +    ""&"+'#3/!#%!%((*/263.6-2.')'#!"$#"%"# "!&!%$"*-1%&-*)3*?4@2/A!520 *5+-"$D((% ,& '!'!" $, $ /' ."'*($ # !# !%#( '6"*(0# #$!,"*2,3.% & !   I)1,7B,1(!!   +   +     + +   +  + + + +     "   +     +  +   + + + +      + + +  &%!(     #%4;.,-cC2WO-?$EB)-+ +&' #&! " ! "#!$$# ##%2'4ID4=FD'@.$9.)?%#1%**.-7*;=(39 C@80'1 * "&++ "   #""')""-'"-13;39>36F:HU;7>>73C?:J@@HBAK@?M22.('+$((###"#" %%!*.--%B96YBIQPYRIDQD6A;'<:@A$?? ;6 R 3P!1E/;=$5;3%)./'%  "$ '07K3J%0(*""#2 -7'1=(?)B19=;&B'(%&   +% ">0'!54(&,)( +    + +      +  +  + + + +  +  +    + +      + + +  + + +  + + +    +  + +  + +   + + ! #   +   ,2-),! #/'3;*4SG&H<#A005$*/,"1"*&(0*(!"(!#"!&"%))(+63.;<-+>.&/0#*$," %"#('-7)05>'78.'.. 0++1$!+-$1-/;(+8 %0%-2.35.19.8=5;G27:-2=18?::D>9I@JUDDH06>%./#(,"*,"361.',.&--.A?=NJBZLF^E@A8438':@'?L'CT%:G#2>$8K&=P"?@" + +  +      +         + +   + !!&# 5'=."'72'/$' &!(,#-,3!>(=.(30(2,(&+(#%*( - , 3 -%--$) #.%)7!+1#!7!)' #)2&!& +# )$!* #&)%'-.22,3 7""&## + +   +        +  +      +   +   +     + &)%'! '#'(*,"'.$-$''""*&3460E5)5#(.//14*.!-'/*&1-,+", %-65(?$- %#-&.4+6())/##D&("    54'??$('$=(&/ $.%)" N5ET'*&%46&$-'"$+'&5)%2(%0$( +%(1&4!  + !#"#&)0=4.A7*0+  + + +       +    + + +     +     +    + + + +  +     +   + +     + + + + + +  +  +  +     ' "    +     "$(4=!-&"20/.$#24+.F*23C8-73*$%!%& )$$"0!"$$"(&**2?,=N2,>FAKH#6L&DT:WLAQ7DP8KN9:BJ:QCNR;69.38)27#:4&-5)1<&8@);804;BFAEWIBQF5H9*85)=L(;A)9C&=H$=A%9@)=H$;G#    + +  +   +    + +           " %#"5)A5.4.",.'# "!*&#$(!$"' ,%&,#5 B(E0%1/'.(,/'$!+5=6&A)@,39,3#$+%")*(,6(&4$-%!% )%! ()7&)#"'+ )!,&-&&"#      +   +    + +   + !    +   + "     !&)#""  #"$ &#&'+#*&#B3!@<.4&&+6,C-(4"$. (2&56:@$)802;(5,0/1+6!03)%0&% !$!!"    '-0< -K")>7<IK0O"0(66"'EP2R/=I9P/6 =;:C9B)-(!./-41()- "  1+ ,  &%($ 6,"/&+-13+!        + +  + +        + +    +      +    +  +   + +  +  +  +  + +    +  +   + + +  + + +     +   +     !%)5IFgU+P_"WO%6C&?L",<-BO$).)'%(((5-&(.#+,.,/!#46'F+> *,'0%'0!%"'(%/-;;P8_v&md.vh$eWcp&Ic?B(0 '!#+34;.((",47%6-#+ (+1'/9%,6'-%/%&."(-" 1),-#A7KF9P86=#8<&8;4+>8,A,-9-/C,-@..B*;H05H#'/  !(#& '$#, &  (((, , %,!%.#'+(/6!-6!"3((-)((-A(;. //!->-@C?UDBKKG\NF_KQ`QVhCFX%.9!)/!$2"'#)'&1%$1&&1%'2))8/)7,/=+2<,0=?7MGCXBDN@FSPOY>EJ,8?16;05=.2>+5E,:J/=J1NG;fG;cG@U=8D?,/3)+61.9/5?,8D'5D'0B-9M'<@%    + +  +  + +      +           +   +   +! #((3 ?, 0<-71 #"&$&/#)+"&"+$./'+!7H&D3%/4,7(+/'$##& -@;"9+=418#)4+ ,))% %'%$,(.%$1 '-! '+   "  !  !)/":$&*+ .2+*25)""1"'/* # *$   +        +    +   +#   $)(0%+!/'$!2)"/*&+>)/@'39&5!3%13!(>6$ &#(+#$,'4*/0#3@5B?C!2E,6!'2%"%)# ""   ' (0,.$CKDe'7^/3H*;?+9B/6?,)0-%).$1-%32$12+80.=5-6-%/')%! &&,$* &.",!!#'#$#  "#& ($%.-$, .%)5 %.&,&(1)0'*74,@>0DA,@T3IR?TYBYOG[.3E"&2$0 -,8)47),-!#-!%5#09%-:-8D.CK,KD26FG;RC;NC?TR?TBCH1>H.=H.:A*5;'2A09H55D#   +           +       %+%)2/"#3$41&=2-2(  &3!&/ #*&#"),./"1)%2!<)40#//.3),.*")$!' #/3$8+3. .7"*<,.312()# 1#42/-+.++#*,     *)+# $!'++>9&E441F):&#-"!+",-#3!,   +   +   +  + +           (     !!""!'&%B5#*-,!& '9!'4!+$+&(#!#(#''&(+/"*6")4(.4'8@'4F(%=(+%4/5*B<5.7!    +# )-,:791>5+GA0$H,*7)7KA 422FJ.:`(13P>:W"G+.<%!$%6+55+2.!*  %"$'#K."99-3%.3!88'/*%.&%4 *,  # )&" +      +  +    +   +                +    + +  +   + + + + + +  +  +   + + +   +    + "3'*?=086+96*7-2<1=-(-4(>43>03?$+("+)%-+,'4""- *!'3&.5!*#*$#$"#$)6 6>#GH77"%&+)+6$+M-D_=]QC^Cm7@2pe\$gL+KA%,/#"'$)" %' &!#('$%$("%!"%2*BL,>Q(7T&5C&'0!&*!$($*1!-&)/+&1+&5 (0)+(%)+00'+"&(&")$# !#&$)$%()#!+"($$0(*2%(.'-7.192'96;C@6GD;N@>O1,9# ('"#++ 2')0 !,".#+5)3:/9H>FQ@KW5CI5%4E.=K@S]\hfXnc?GR39L*#1> !     +   +           +"!%/"0)##$%&0.<0!*3,'4 , -(,1#&+, ''$.(8("2(?&!42#4&%,#"("* $"06+77"02'34')<.+5&,.#+-(,>(B4?8+37%.:"#=-"!    !$$ !$+#$)('229")6$,96:$'4(+%'+./!4 &#'+)     + +  +   + +     +      +"!#!!%$!!" !$$(+535$1#* 0$,!#4")5')5#*+:8%?""%%#1&(7&&2#,  $ + +  + #+-938F_8_%(;&4;6D8(!#"1 3*!(8'0&&3' %!"-.) (&7!$(%%&.>*$/K$J;)@E#GN!C;+,!)'>+,*4135+,$($)"& +     +  +       + +    +    + + + + +      +  +    +     + + +  +  +    + +   +  +  (-&*025(;>"EC)3<18D%27+&.0/:)&0 ->$#/"%,&)&&("-2)/)-&'*13)IH3YB26B%,:$"&+)(115D$06$27 27$&3++>,)@88N>SV.OK 6B9;)""'"+'#-',".#1B0>L:NZEQ_I64E4;C47K.@G+6A&):)29*AS5KaPJ^OUZ:KM*7F+=G,F?+D@+?=)5=%#+!)!5D5>!  +  +    +        +   +  +%%.4#"3/*>)53)!1.'"*#*0" 0, 0.)3 -*',/*$:";'4+C*$7-"5*'+&-!% 9=)7,%70%-6$.<,13,$3 &/,.318;$>=.,P(?+-''# !%'"    "'!&*&.'6$0!*&3"33"/8&,9)0!+8(616*7'0-*1      + +   + + + +  +    + +   %   $% )( -',&.  %$)/#26(7%* !"#"!* (.#2&-8,32- !,#) '     + 1/ <6-:4+43%0$&8 "4%00! ,"($$ *#$*+<7$%&"*^.' +*X814(/G><(4B#3/'(%'<')9+ 5!$+%+(*,% &!  + +        +         + +       +   +  +  +       +         +   + +  +    +    +   (,0;0+7:%<(3'74!4$/:"5:/5>!9<%4: -2/238' .6%0?*:!+!&*11(5D>Fm9lm-@H"653,14023/>MeqZvjufpVNq.Yh+4T;(KR3dC&<# #!#$ / ,;# 7L#D?*;'' *!"#$ &# (&#"(!#+%(!#-&$,(")""&!!!!%#!#&!+%#('-29$MV%2<,.:-4D-0E'=C,*8''4+29*163-;S?^MLZ1:G,)E*7D$++ '4/387&(68*=C:RJ=`HX_BBI94C59H38J08C15G43A0/=/6E,7>#54)2./E@2\O;^RCSU=AM-48)*5-3;1?I/EH/B=!..00.3!3?'     +   +            +   ##%1'3#,$$8$;3-+@.)*(/$6/:4J>B7/8&0/#72 *69*B,31$=&$9,!1,''' %#(< 9)1+"/9$:,-0")#!-%%($#!*+#*2),70>34-"  !#!#   +  $!(! $!*",()**%*/7$46%*=05)2/2 +8 (3)     + + + + +  + + + + + +   + + + +  +       % "&+ +&/4 4* )!*! *&+,"0()!--$6+&-&)(#!#"$"#%"#       +   A80E)/$%*! $! $(*)  +!4"&#*A*3(;$5 / M)*#'0{6(h`)HF8",'*)/% !#      +  + +    +     +  +        + +  +  + +          + +  + + +   + + + + +      + +   !$*4!9%$'')""'.&)&(1- 3+*7*4;+%:%!/(#/'!0##$+$2,(6&(;C549V7NE6,% ))'C6.AI$QB'"&--,.9)&-C#4S#=A'>*)31->!$- )/)>C#9<#FE.9@14=B4CR5UB-=>@B5+A@(3;-08,,7.,>+7A)>=1C:Cu\?Z<_K7(--".&"!(',-/:06B5@T$FM!CI!FE$DE-   $  +   +           !"" #$!,#6*47(+952&-C*E6 J>"C;(<<)%5) )+")(&+);%!:(5.&<##@,$9/"9+"''! '5 ..02%&0')())"!'$%('(!&&-+'0' )"'-!!     +   "#%%*)) "$"!#'*'!'''&"'!"!)     + + + +  + +  +      +         ##,*),32"1 %"#-%8*0328 2)"/((9/ 31&!"**.317 *6'3*# + +  -9#!<.1+%%)1(&''! #"   $!7E$99 (8#"%#$,?$of+4T5! W9nfaGCJ '$ &'90').0(&         + + +  +     +    + +     + + +  + + + + +     +   +    + + + + +    + +      $"#'+.11"-6#, + #*8)*.$(*#&##$( '!*)+"3%3I2.J52F,/:>2F;-<+#1-%7),,EROLCQ@flKPgJdV8BE5?7/L3' %##>9N:<. ;;+)$/!3'.)4$/%0*/?$+=%'1 !)"1#&$$' ($#%("&%$(#/ (, !"! !&$+?32(*#,KY7OT>O\%:5!25)EB$33)$-1,2.+8"-6(->$+>"+*&(@4FO:GQ/@L*3A-(8='8;0@7:K1/B3VO2+50/9((/('7$*3("-1%82(7:DATfZjBZC,$#'#"+$.+1:5.<2;L/;O-EW0D]4   +      +!#   +      ! !%&#%0)5211'1/0:+*F1!^?(YH,3J1!85,2 (("0! 7'6'%9&7(!3,%1*&(& "'%",-80!83'=;&4@$; &-$"+#* %&&&!,$$%  +     #&'!! -! "!,!&+ %+$&"   +  +  + + +      +      "&    #   + $%'&33+3 $7(/"4")/!.!,&!)$/%&)*!"("()".+$'*!"* ',78&8 $,!$ + + "'%""9% "  $("# .?/'LX&."9_JPlfeLNE'&"   +   "&0!#            +                +    +    +   +  +  +     +   $# !&,/!)-&(8 )3$+*%&!&%%$9,:(,)37 ",+=:>A0? &(6--5%7.4;.:?+DG(/6"(./GA0DY@;UG4?G8B?7WHJZ5NZ.Oh5Kt)RWKA=I@Is:^|+X`*7:#)#- ,*.0'-)'$)$*"#&()"$ $($!"& % &'&$%%"!! #! "#'&! %/'A1,F)H]6isVu{E^iCtu-fU30H-D= *5#%2#&1''8**8$#0&,*.%9)?;*?@8H.?B'1-)&*'.=74D4/<6-I160-%*&&$)"'%$!%+'+7CCVkshFZ='*'4/ 520/#.-',:0:>09@/?K.N7       +        +$'""!+&'*(1,#,*)8/+?0%M3 S?,>G4#<>%6'2+$2$!5#=(:) 5+$,'&$! '#*+,"822E%)D.*:5"40'%#$ !' $+       ") " !! #% !      + + + +   + +     +$ '    + '$( $('##2&'.&)(2&+!% "#+(#&%)&/*%,!"+*(-#'..$.%+!%+77&=' (   "0$$  <#"*2@$3-*$ ' +! " !$%9%+ #4$\4gSC93`PIK-0' +   $+(.$1%  +      +      + + +       + + + + +  +  +         +  +  +     +   ! &(*6,-5213/1$0&'+%66%.)%'1$,!-%- &&(56$ES!@D-D &C%2D'#@!"1&2%%/&)%.7.IO7fl4X\RP4DV3RU2LK1FB"2.#!"!#$$"( *-4E@ZrijhHnD"5( ,4!-:!47!18$59(6>+6C,4E-4H-4@/    +    +  +     "! &###%.!$))'50*C0%?8)D>)5A:(:7**!60&+%6!4'.(/#$+& ("%""($,!$5%'3-%-4(*,$%)'".(,"0*        !"&"'!!    + + + +         +  +  +   &("*'   +    "*1/!$)"!%!!2#(/)1<46.%8 .%!/*!/( ''"#$$,7*4%#"-&%'#"+"(+'  +       +  --2%"#3./63%&)$#.W4*gOj]5,2E62*$     !! +   + +        +   +      + + + + +   +   +       +   +     !!!')(+3;&'6$#)$!"%!,9#:M".<&05#&-(0 ,4(&.*-B45Q',7%,!"6& )'')2$).")#,/*)9U6`\BjK3_=!;84\$DQ$F.*5((2) 2,=Q*.<("3$!.(5@+072IH9D7(*4=*SULmBJSI>+,)#$(#48';E&CM$8E%6@*1=*-=)08+$/$   "  +        + " ! $ "&   +#,)&%#$%!$/#-, 41+>:$@=$56-186)31#+%$"93)1'7"-$,%0#.*"%"$ #&)!&('*,&+(1$ 2&+'(+).*11) +           &""%     +  + + +   +    +    +    $!&!# ($#-## #"*(/(%?#!4 ,%.#5 1+16(73.3#''#" !# +  "    #! %% '-+ "4$&#%,62#+ !     ,35d8DZ9=6*/*&"!#!          +  +  + +    +                + + + + +      +  +           "#$$(!"!"-((0#/,)(2-#/()80&1'.0($"#($'#*3$1)8(4,%4!3> *.&-7)>!'.%&2"+!"%+7$/L.5G,3;'&"+".4%)##")6"*.$7;58C'15'4")40*6/.>Bby(OZ!==)*8+8HGDTJJZ/6#+0 -6+<4:C$',):@02A$3/398HAIQ9E;5CBA`PVqNNWAAJ75:'+,))"1.%!%$"90:ZJEoIF=):)#,$))!6:&@H,@R*:F%:G'9?%-4"(.!++" +      +  " $"  $!#&"   +  $&$%'%("(((&-,43!18*3;*/8)+/+'.)$)*#$"!& ,2!,(+!$;!5,!/%"/$'!%!%  "$#+)(!'&",!$,"/+(0-#/.)%"          +   " +    + +  +   +  +  +     "!#!$ +$2( $+)1/@32)$.#)"-%)"+5#/(),,>3G/5/&).,2,2)7'6"5&0$#7"+&&!##(!" +     " +",%)? !('./.I*C!  +  &)   ) & !   +"!(((9J;2@>(#'  + +    + +        +    +   +   + + +     + + +  +         &!"#",%%2&@= -7*/.'2'9;'&/%05*415(4"$-"+ '$)(3<,,@&$1&'-'*#."$-)0;( 0#':""%*#0).$*5!$-1>B&)813 -% +)"0D'@W(A>5J*/?&7P34SI,EH+C6 1$/498"("!"")!"##$#)%!& ("'!#$! !#  )# *11 55)@E(52.*'*)&3#2>=BM3m_"@<?6'.;9;I,C>+2=>48D4;I0;A7=KKOmRN`hQGxQ2N. ' "%8;$JR)CP+=E#9@,47.1;%4<"3:2=&       + %"!   !##'%%+#(&#   # # " #&%$# !%$(*..%(6+.'1*+%,( ,,!0322 +3-3-'&$ %)%"#&$ %"$(,*(!)+)!!$&$+ # $    +    +   +        + +     +        +   + +   "$*&!%%3 $+!/&"$'(%6.$-2&/$')& +++&5(*"(4#+/&':!6B+45+45%&!.+(3-$"*%!"!'%  #   +! (/'073$G(.%!!,&$$""'/  - '#        # -+(/1%,8$  %    +      +    + +       +    +   + +     + + + + +   $     +3 $       '   + +",*;)8)06,/(&05(/)#9&0;+!1-'5'&.*+)006!'901)&!& 67 84'*). %'-&"1,"!#"# !!"5&,&'#'!%%%/(6B%?F *!(#(,)1(1'$()"6!,$'!', 4%#"##&)#(4:31('/,-$+0287?06!5=a_CB-1'$ #&"$!"#/7&??0/;0(92)-&&" /1%*,7:L86KE6O?9=-36#%8:3NU9"-"*-$CH*:O.2@,4=&4;'/2*%-($6&06%$,+        + !&   +  " '&"+,%$$   +/ +#" !)" $"!!!%%&(0"'-(*+&).$-$%-$+-%,+$!-"#%&!#$ $(!%'!"#( %!#% #("(%%!!       +  +     +          +     +      + +  +       "%"& %(/"*,+&."(',) *"0*&5!%,!"% *3,<.731)%"! # ("-2+2;F.3?!*=25),>*7'1'4 )6&$ # -$ (& 952/903I+#3,'0 !-&=):;.*+@KC $D -<8L@  +       &(''0/!." "    + +    +         +  + +  + +     +     +   +  + + A*M: +:( @! @@ iasyQlo^PT  &#""   +  +   !4--1?- +(A=)=B&86%$,0*<'5C(-4%-:%,2'6/$(%'-$!# 1C+7G'4<#PM<^#>G)7%+ %$%!!!"!!",)--!$,2(91#,/32.B223#A;*EB/>F+$"4)/**4+%$# *!&/  $: $    "" (!1"%3($')$#"   +    +  +  +      +      +   + + +  +         + +    +  +--%5=n(:e. (<9M\Hw^Op3Gp?^/, + + +  + + +  +   %#'0*-5)+"1<86G29H*"*$!+/&74(($2'#0# *#3 ''*&1,14)538P52C-,;91E$&'$#&&#)$)'!,!#.,6M%>OKP23/)+',<)*E %#+25.0(343!#69,4)!0'% '$7=.0 IL71CC6^B8T;2X+(8%)5(1'+;V8?[C7M2;K&!-1>#%+%())1*(;10)60&-(!2A &#$ #'=5)=1(CA2IO,;@/KP7SV9KU8NV+D83>K2LH%BC%00,#7".5%0B1BMGSMYqHah7>F4:C.49+/54%2=4ADEM,X_%MA&)/196I[Nd|aamRDaA4FJ)?Q'9K&/;#(0! +          +     + !#") +   ) #",!* (( !".$.9.5%.6))6',1"++ */1)2%;'G$-*1+3&6&/''.' & %#(& +     + +    + + +   +     * :' ,    " =9.5?(/:C8D<<6(1)1 ( % +  +"  *',-+40&%!+$$#"#'!!,",.!#& >&%35'>B$2D 9"$+% -30!%!&'&*-$!!-).!,"%$1#%+(- /  !     +   (O'JD9\:8+B4!#$  &%2WA*G203/3%".!01,;='"  ' #' 1"3$      +      +          +      + +  +      + + + +  + + J /82R'?&K%V#Xn + +     + + +   + +   + +  +268 +2+'7&(7,9)52,9(12,""")+ &-$ # #('/!(4*%4."+74;.$/)"/(2B1@5>#&;$&$%+$"10:?3L\6=Y>J%>T(2>$0#,)A&=)8 ,7$ /H'1G/2,.:/$*-3-5$1C%A\9Oa&4?56A.;D,=;23+44#>E7DS8^u-X[(oj;IM/*.!%" #%/#.>(:S*MgLf@,96+22*5-%+(!$+$*;+ =]#/P-&3'"##$&./-*:/! !')!((&,##)%.*$.-.!+       #&&#    -& (3* !% 2&92*@) ), *#+! &10(800)-:C0N!,/@4CA#0%/5@'4F-; "-,"4       +               + +     +        + +  +       &"0     + + +   +  +  +     ! '.#',/9'$,'%&%#-3-*+'',(1>":F!3;$*3'+"+"!*#3#35(?L3XW:B""()!"##1.6"*$.%1'9L"DM!+6,.)-(/="IJ;Kd/[g&LQ*Nf)G\)|OP1A)Cb5A`<:7)3,'%(&$)%)&)--0/7.,>+/9'..AI&%,1"/.!')# !)).7@+>L7C]:5O0EX+M\(Yp)29?XO73!)074F]8@N *3 $) ,1$4;%...#36$$(2/+/)4>2WS,;I!\f#Q_!3<'4X6*B91I7Rj.I]9`u:qB@f&2@0:F2OX0D\<_|Hh1K[ 7; @9!WM"]FG;IA"6:$'/%'1-.:-B@5=LAWm?[s$X[TI9*D7,QF&"'(&:*2J:Aa@Cc?]TTgVRTSPaUzx8DPC5e&,F1%*A36),B6+8J<0"#4=5I=Q*;E@=@&-H#7; ;5#)5 $ +   " +'' !/'+!4 "3*,D%=/#""(0!).(,'!)(&)$!! $) $+-!$"(#$, .("*%&+%#(+50""%      0 #  %     "@Y.8Q/4W7-J7"1+ %'<&(/:) 5%)#(#).,/(*?# +'  +           +   +   +   +       +      + + +     + +    +  ) & #   *:(4'#,",'  +  +    +       + #% *+]B@C 4C'6M.4KD5G5/H3VT/@9"6<+0>>,0 2''+#)"# )&+.24)B>7@25;<O,E[21]!3B(9-1F2EO,!5'7H&BK )'-717'% )#32"CNBL#4<3Gh65F16G>+P4.B $)4 %"$/#6;6M7/PC;eHLd/Ic)1G%1I1#>8$CO)MG1G/lu;!/007(>I,Mc0R\6QnCTv=So3c2g`*CA78A/),*("1B6RY:QUOZP]QaYLjIhsf^wK][=;MB;]@JS*M83*3,($#% $."'7&/6,2<60?-8<%2<$75)44%>3(2,'00)376IDgmwlJM:>U;50#$ "%$#!" ""!    +     +       "    '# )(%&'%%(#)/",%*(%/&"".(""! % $&-"1#,2%-)!-%+#  + " %.5!6#!'         +            +  +" * 1 $"4K39++" +(/">.#..;4439(11*1G/G8+F50C=-I-B..1$$# !! ! #6!+12)8*#&#>*+(',7!*735/H;6^R!Z[HH>D1A4738)(/>P>DY"<.07924+=.3 1))3!-%%               (+ : +8 ! 6. !A-(/,&/-2%13AI.B)06 5E4+ *3)%'/2!- *.&+ " &&*.%      +   +  +    + + +    + +   + + + + +         +      *!!       +   ##%*,.'*>?-5('B?"<-/G*@G0LE9EJ52=41BGMa,BO-22+D; (#*"13"+-*"$.1!'* ',%6->M-3@0FM/F[*6?1)?67:,AT)0?!;F/1F/+/*16=.*:*7C"/.&)%$5#5D&0!)>G(MV3HO-JW.*945:(EE1D>*"2 )-%1$+$'"3)'802C07@3)<-?I#37(*-,4*-6,7I4AE1;APY}T\lHaAH-RH^r+^U'UO @BIEA7915=#5?%9B+=@/?A':8,29.31);3(41*0-+:59WLdswjJR?4[8+%%*33"$ + +              ' &!!    $"&%$+/!)"#)'*#(0)6!!2 #! !(" &) 9' ;&8*7% B& :'(*'.   $)#"!    +      +     &$ +  ( -*,!3 0%'! #(75$+%+*%775Q+&C0/$$),0#0'(7%14$(% 3(-&-- $%$!'0()   #C3@V+Q)6:8J08N*\G-FVCGG7.?014.1)'()*6 )5'-#*:=,.0*4-6!;4.+6',0#)7$.) "!$      + + + + +  + + +     + +   /9KA?$L./'3&4F&9(,?#40# & 3#)%#@#:*5?4I$*$&+"%.+!"!%* "%       + +  +  +    +  +     +    +   +  +    + +  + +      %$     $#&   &$##&$02EO)PXCB%EA!12&676Ie9G\4:K*J7LW.W_(Oa(;@34(2!&5 GI',)(+0,3%-7))&+&8@1DS(W]2Vj-=W(AI!C<#2F(/>%4J,Ee+LU GLJM#fi$vo(?8;:"G`QR"'2(,@.)06 /A1Vs5Ui3D`/,=!-43bs4c"#)/5 49+#EJ"NB&)(%40#/$086:Y(*1!%,'3$,A-/;*24A=YMMzU8]LS:(   %   +     +   +   #!""     +  + +$+#&' (&''"*//$*:#%/'*/!15)7+*/"/#)) :&A*"A1#4/,5(#)& !  +  ! !!         +    +  " &!#'" (&6+&( + (#-"  *1"5)(+,(,-(2#+77/5! $%*"($",3#//0+6//@! =28)3"#!! :%$5"9.$!) '$&*2++7C@:5!64%'#"!+#!11*'?/4$4/ /9"3;'(8"0&' &,'2"%+07.''!         +    +      +-( #&&!E1+./()")4.*!*$!(+1$"-2,&0)3,#%5   +  , %1!)* +  + +   +   + + + + +  +     + +    + +    + + +   +       +  + +    + +       "!#$+"%&!1,5'8B6Om6Xs+Sd.:F$GV/[o?Vo;IT-:J2Q_4D^>7\@5M95B%-?35"24%2A.RV>XEG-4*."#)/ 9<.]q6c7x4K\&K4Q]66?&(1"%-% 1-.7'/;-.>4+><&5,,7*3A5J_B.OIZpMjKfGg?Uy>a}IqOVjI^|Ni>Sb@OX@GZN\fVT`OJW:J]4PX,DD',(2!.L2FA-2#&.6BGQJYlQ}\dz6P\4;D;EM4MU-QM,89:6A8?@76>/6<'**"-*)0*/133:;Ltnz}pYX;1(""+(&$    +       + +   +       + % %)'#&,*%%!-!0 ("&'))2"%,$('.!5!.# ."-$)$$+&+2!!$'! $'#'( *"   + +          +     !$ !$%  " !""$ ,* 7'+/>,7"2&#'$ !#%'%*''"0.$ + 5%+<#1 53B)%#&'1#)*(<!2$##&%(%/(*8$1 6.%-3(2/.313,;-7"-- )0!6 $))/%(+!%(#"$$$'/"*!   +    + +    +   + + + + + + +=&$0"#+&   %*1& &( 4'/! ;'"    + +          + +    + + +  + + + +   + +    +  + +       +  +        + $9        " %&!!!#&## )$)'*-&0% *'$KK/BRC6IG3O74J/3G(+8'.E*6L)>*664KW>b2ER 0<$(8`RLoHP`UOeVXbFim@K[RRhYUfIN]@PaIUm?V[2UQ#GA,AG0AD !*37CC@I]GWkFhQe|6_W)91)3/#2,#*.)*+.?7TdYqtq[Y]D6 1.+)-$ +  +  +  !!$  !""' " +& "! $$15#'!+ $"!#*(-#/!,'-'8<%6'7#-"%"1">%0,%!#!")%   +               !$/ + +' " *'*!%)4'/5->(@-,0&,$   #','$--#,..%+:.8&7!+%(& +:7&#+'- +3,,"*'4 '2!('"./10"&; 3&!+"8-:,+*>$)" #( &(!      +  +  +  + +         +   +   !      + +  +   +      +     + + +   +  +    +  +  + + +      +  + +   +  +  +  +         +&/'1OLJFE   +     " "&$ &/",%1"$3#)5"%+$*1+B>#6N5=Q0:J)/F*"1&!1-&76%Ro&LM(R]$;;+*7<>P.KR7AL8IO5_hJOq=4WJ!B*2."(XhZM|93B3,@P1MM0I86I59P<2G3#/%('&"$("$( *9 ,5*!24/A+%8"!/4(7:#:+"2*$7!( &%$*'%3'!*$*2*(3&+8+-9+/94-4=(;8%:<9P0HU20@@OqMhzOzZPbrLQdD9KKGYGYrH^oLGaQHhYQ^>FV:CS4XT)>5'87>5<>ESLJX_MdOVkHbkK\fEBG@4AENWFBJ;@>3O>*G@)E5">2!0,.G@EyZigT]L@BI4$-'#:=94.+)*,-/3682.        " &  + !*#,(&- $+(!    +* %!!$*"'"&$&$!%!##&&*#/-!&4$B!G+L/'R*=-0#!*#"   $      +       + + &#  *,,7%% '#% !3 +$%%+&09,;,C#(8&+:.(1('85.B3)7!" #! ' $ # ('*&,%-/)"38%7=,=%$3!!' % "% $!++%>$+<.&H".$&4+&4#2'$+#"#&"!**"0/))+*/% +$!!" !*  # +!)'    +   + +      % +      + + !  +  !%   ! +   + +  + +  + +        +   + +   +  +     +          +  + + + +  + +   + +   +     +/)(8!'7    ""  $!'*'( !# %'%$$#%.7'+/@%-G-!4*%6)"6*"/$+''&".,+:8G[ioBi|2e9NqA}XG]r7k>ApiwIkbAR?T`7ak6O^"PR)F`/S_-[6aw9HtN4R8&5;5D\JhMVs4]@s4]vONyIP`*'+*('-8"(,#5IL?P8HQ+LU,IL15=*2?06>13='*2() %(%,''!)%*7!-A.8)!$ !'! ( %/$!$'+..8'6@ ))'/=2@S-GP3H`XW~acdYs\XM^j:3=B@SFKiO^lOZlQV\SF^IHSBGWJdl5lk,mm0\_8PQ@UbLANQ;KK>NT;XH:G36;B?IDJI>XO7==3D>0D?(436BCT|dTdDB4/762658Q>e~CtIhv;8T3[_@Yv0Ur=UlMdd@=;)2="[s1[xC]6aETpV8WO=C<6:6S=$"/4,17E[\WcvOhyOtFgnB`a:DF74975999?@2=4>C9BE7>@76TIaiPxM,7*"&.&:G'>O*>F$08(/? 4532(*'.9(:B!>G%;I( +   + !##&(% +!$ !*! 0)$ (()$-#$ %!!) ..( *)%&+4D$W"f2 dE+>B703#"5*"  + + +   $!!%!)%#     +  + +        +       +   ##(+3.8127'2$00&+)#."*/..(%&*/#"=/%/*(6"#5')<;/J%&$-..+%%-!(;#3./0:33:#8 / .&&#$& .+$7"/&%'(-$)!'9"'*-+#1*%!,")006)6*+$$%#%4'5, % " +))%%%  "'. #)  +            +  +  + + ! + +    + + +      +   + +  +   +        +      +   + +       +     +&6.H >i   5 + 3'4G26')K,C      %&#%& 52# * ""44"0B'CN!IK88!<=&Ik _m"ux&OO3.?5-22J]GwoZkUxkQthWki|N^O;T:2@CZSAW]IeSJ^?AY8H_;?Q9>O*7A$)+-0>"'9$/9 *1&3*1 * &     %/ )015$16)0D--:$+!,B?sKBiq78C1,56',,&524JKWu@p;@H75BH8LKAV;BF6:N=IT61ALHfVs`d}V]pBRj7[e1ZZ-?@-08/59.<>6=G7GI;]X:I@-/,+*(-,/124;ML_kQ}T*2'"+/*9;*.:*.4#-5#/9#=E!>D"=C'5D%.9)3A-    +   & #  !"! & !$%'#$    + %"#)*$= 6)E#%Q$`1e9+3@9$,+1("  + + + 0(*6!3&)#'!     + +         +   + + +   +    ! $(!2)13!.9(&1()5!0!#*# 0.2A-3:%*  +)",4#(:$"0*(&O5SM!5O28909?G;:N+>0+* (#!*!"    !'3'!## 2/ NH W*(*$$3 )"0"+!")&%(")!."  + & !!%"6 & +%0  +   + +      +   +    + + +   +  +  +  +   +   +     +  +  +  +  +       +    +       + +  +    + *iF==73'G)%"38(#&2A'20$1#,#   +   $&#../5"/)1#9?VCAULdyXJ\8^c%{!|6k}aUiyHPyd[iFaRKjK11vk[^]sK]sP]tg[fJP4[`Y[eTba4Tl(M>E2OfT]Z~>?Y*;@#::-1&;FBNuV_xOH[1IM/ET:5?=3E19O;G]23C1.KNm]qXqPqNN_z6CK/672B@3jc3KL;P[MeeLQK-0("+#'%#+&%=<<`r]RbM/1'!,( ,/)39)19!,0"2<#/;,5?-2B(9>*/; 0:'08&     + +  +#      #%!""$#!  ?#G+e(&`1b2^<)2A36!(=+.%%+1! +177435&9+*81+A& ;(#' &#,!   + + +   + + + + +   + +           $##"&)%"&&,!.3('7$. 0 #,+:!)%'%5)*.(614-#;!2'#/"1+ 00/1>?I0QD373AE5Q#5H&.I7.B.8!  $!      $*2(! !.1EB.#FK' # * !")(   +!   "'&!"!  +  + +  +   + +       +    + +  + +  + + +  +          +          + + +        + + + +    +    +     +   +  + + + +  + F=$J[WEl>=a'ci!L_ 1)'&'$+<*""  "!.' ("&'*#"   +* (5-3+8 FO.=],8M(:#) *-+ 5%" "'(!&-3!/8#& %/+'A7;O?DU9AK19K50N0E^&[d0OWC>MY7M<'?58O-S\'?E+*=:/LQBjL`s9Um<\q6LW+0@-3@?7M14C(>M@Us]WT[zZkFnw.xr8szILo{@stXf]\.=>'3*(" .#-BB4=!3B$69),:02=.0:*1:!.7#/5'  +       #     ! !*%!&'%" "(BX%i0`<#a8*L@*D66A((=/).%#8;B:,30+%,E7-:038/5;)+B&!/.) * " $     /%')254M1BW>2$$+      +   "   !("   + +           +  +      +  +  #            + +  +  + + +       +  +  +   + +         + +  ; SU]?FZ;-G,%! !.2QSA)2#=9%1=1CNAE]Lnxr~FjWYP^m8D]BEZX{7mS%367GLUtTlmWO[<^b0Rp-iEqWGo5WkQU&Vc)q;^d>PO=RP4BW@M^-5E+1K96 $(-",9),36/C3'85-C>)D93'.22G+%21(:%,4$!&#$"",2'29#5:""&1(:J6JA?S=NV8KQ67LBNe9P_4@R/6D%+3+,7:@X38F(*)!/9$:G8VCNiJQrCUo)BP'IX5Tb(ER+Tf=sYgm`UVUSbaS\`{[DT:44$/,".."A10VCJzWAU<$,-&* *4'*"0:,GH)06'/3-6C);;&08(0.%%,"&1')4# + + + +  "     '+$!$  +  + + " ++CY'e0"h80N8,E/*N)1E3!A:&-20'!&;:J2J=6K;7KD0:@*/A-83$352837C8'M",&##    +  +   +  +      +#)7)-####,("*<""28G?%N&$,!!+ %*#+4#<**%#!*#';%,)",*!#$*'0*'6% 3#!.#*&(#!+)5*!1 "    $#8"9>2H15$<*5% %60% .:$ & +      +           +    +   +   +       +  + +  +       +   + +      + +      +  + +   + +  +    +   +      + +      +   +       >LhR{Vew0gk^? +WA P>rnDHiKgM'"!%%"!# (' 2)%8 '0%(##(;&*='B$ !  )3);=6BT>F&7A$NN.DH8nyv~e<@^D-H>%4/2>B3Us(3%,,5#0HYmFVm=Nr=Mx;JaLtlfmaI6_4C]5~Gx`SeDTvKY8lc2\z7Ff(9P$")4)4!-!%&)&$&1#)#/%&   %)"($ ",)"3'(9 29'*!&05,8B*@B/JD-E;0C>-CI5SCAY'8D%9= -62$96-> "% '('580IED^CUx>`s;Wu8Nh:^}7iRY}h`m\hr^mchsth{kgZZfHVZ4FF*KB)?=+PA;_NP\?\@!(-*,.3:;%;>5OQ0:=&&%,(,-*9)0='/5!$',$.3'        !     #,)$$$ !!!       +  -M\+b5$j5*F=*8.):+(F2%<85526.')?,0J$&N;#2; 46,273:1?<,7R);E0?H&6H26L.)?+&<$(@,%6,1!('       + +   +    +    + $! ",0&$ $%$%"%"$.('/"20'BBA"~fZG_\gr*?*&" !##) !"  + $ &('$2!/#6E-BT$#*#*"+7:;BPS4N[2H[0O[.[^5}cEO}clas[lTXl337&2F;J+):,"#(; $)8@AVeD4NK:H>ESDAXa>WaJzI7F(E\JSoeSch9ZDXdQOZL*P@':?%:5%3",(!$.&#!  ")1 ! #$'&6#,81:'&/'*0.*564C+8@/EF2CI?DP3IS/6@%*3(*2*'3%16+-16:A&3?.=Q52/21)((%/ 9*28 !(, !#2%-1 4=#>G55J48H5/J<&;3 70&*0(4(!0$"+(+ ()"!   +    +  +      '("65.7(64-340;'<984D&<$+* 36#59"8D12?+$:#.#"$, +  + !0% '' !     (# 6(0&&& %! )+&7+3!5        +    +     +   +  +       +   +  + +    +         +         +    +   + + +   +  + + +      +   + +  + %:5K6>BD0B&(I-,@83,A:F:@D=m{Ja>`UO/B*;9( . $"  '%!     !! #'@B ?E(?E&-7",5 )2#/9(5B08K7Jc>^l=gn?"DC 3?%4K"EM5=#7H#@Q*KW2I\;Sh>Oe7JY0GV@C\QOpQcS`DLd8I\CNb5Zp7^wQfU__pn`[bEIS}gXoRJ=9,06&.4(?E.A:6LO2NF+@<$$*""!*.#!#!%&7=0SQ(     '"!&  + .#-(+!($!*$%    +  + +        #!$/(/!(/<"7#X#_0 m8+9;(%%%5C%=3)(.4*%'*04*)##7*;@3H#=;'9G#8>0,L0'<59*'&$% !/ +&# #'-"(&     +    +   +  + +  !!'/&&! 32 0;$58"/9 2?#4?(5D8@5G$1F)0@&7&$'    +       &"#.!) %&&"'!  + '! +$$!& +  $!*#%$#!!!)$!'$%#)$)"#"&      +    +    + +        +    +   +    +       + + +     + +  +  + + + + +   +  -% $"#     +    +  + + +    +  +  +  +    +  +   + + +~Uwmj9*6W(5H/A/FR*We-IZ8Ns//3QnL;^bbECy8[5.0T7AD*BT>Y $T("7R'Lc!9R #(#!&20'&+"%*&+AF(?W*AL*-;,QQ,MY#,:!)3%FH#4E% 75N[BWK8VC,G,4CFGXF2J9;A!'"..@< H?&=DPM?N#Pf%,<+8Q)LX58F,6=- -'!1$'27Qi.3>.-+(&)#+4'(9&   "!*',%!"     ! (, &".)3,:L;5J8?U/>F&-1%)2*4>+GJ,FL55GH=V_IrO^p4W_/>Q/BO/6G)4?-8H-AO*GK-@S1Ib=L_9QlBZm6Zl3N]9DW<>OG?_SPfCE^9L^=I`AZoG[wMhFUqZSljjtqtuth^TlR3EC&40!6;-WV3jm7IR4RM,02!+),,A;#.-,# +-A2(#,9&:(0*" (   +! $%$     +$ +&)" ' !*7!0#)%%     +      +            +  + + + + + +  + + + +  +  +  + + + + + +     +  + + + +    +   " +*  + +  + +  +  !      + + +   +  + +   +    + +      +  +  -j=q|"if2]&3S':.5P7;L?O4B4IQ5Sm)Qd_Lh|?Gp70R*Iu6)S,():"'/'-),23&## %12(! !+4$*+("$*4- )B:3BM0IM)VX@]v.?U"%0#&41(#&1K^29K7-5"8?%7K45B*$&&"&#!&2:>_BSw1BaB@L'AV2-A#K`0K_6Uq+4H +%+0/%(?0@&-.$$%!( #   "$     )/149?"34" +@06&7$38+@->C'>C7EVCD^ODUgH_lHZ`HGbJHe>DZ:?VQU{Ydixp\]bp{r]5uv-7|?HyBV^6>L-83#EF'YV+^j,TQ"5=;/#(.7);@     %#,$$, ( ""  !!""#%     + +  + + + +  + + ""5-'D /"-&.914&3(D)!B*$G-!?1$:*(+!%)9 !1) 4"'$48#1@6%@< 14"*&(( "/'-!6+#%(+'0(."0#+#     +   + + +  + +     + + + +  $' %&(#(0($1 016K21D/8D/4L20?'(7"2!)!.&   +   (3-"C4'*#&2*#%, ! +  $%.+%'$ !  ! '"&( *%"  #&",!4#%%( #    +  +     +   + +  +   +    +   + + +      + + + +   + + +  +    +    +   +  + + +        +  + +      +     +  +     +    +  + B85-",!I?!8:+6**?32K1Zd:Sg>RaAGP(9=# !"!B8-1"/+*,+7*:FDY*KQ@<BA!/8  &#            &!*0:H45F2+ %).1?0AQ9FSIH%ET#I?#.27      #(#$) &%   "$" $             "&$,-$ $&)%5/,';%K)#@0&N.&D6*3,,;& <'-)!)"  % +?.9I0 H5 )(%%.-05)9(+#**/-16 6'    +   +     +           !)"#4%!%#(.1)%6)78)1@% 8#!')&%.*#+)**2 +&;!") +)-/->%-) ## #% $ #"(*+ *!%#  $#&   !& 0 !&""!#%#" +   )  + + + +     +    +  +   + +    +   +   + + +    + +  +  +  + +     +  +     +  + + + " +) +           +    +        mG I3 @ R< $(##)(!"6(!0+#8H,(-9!!"#/#")(.""(# #"$!33+: 1!#/&+)7.F/?)"'%,"?E3L_@;^Av+>/$)8A>,F;*>-XZ1=+8A#b_06"+58FP:Pw.Vs7'C ([I./G7 +      + "&7@56-+)6?:;A54E7:3(#45%:-%++./72>&+=%(;&)4!,<#A 0*$    +   +        +         ! )( *,#12/<->++@$93(0"!!1/ % %.&6,7*:%+4'.6+45"9# 7<'/'6$,7!4"%$#,-# +4. %A$%0 3!*+* * )" )' *64          + +   +  +    + + +  + +   +  + +   + +  + + + +   +  +  +  + !!!$ +#  +      + + + +  + + + + +    +  +      +  +  + + +  F!}YV>*Fe$RB?3j$`s"8K"6J2%* #*&@6!9.Q'v^7 _B$(%"%)$#")'2%.6%+/!  +. +'(6$.&-3%+13*:*=I:SOEj@p|$aYGF/$ ,$1#;,);TQ#A$".(2&EP#1$(3/9C<:N-,=;6O88> DL-`ea5'      !!&'0.=+=E+7H3-54)34;R6FZAA77&BJ85J9.?>G^8BV6>CK*:Y.NTK]G0AC+MF?_3PV*KI66JH3LSYFTk50D3/A,8D#=C!GG5HUPS\>7I8CL7GSI_~dyenfRnJ[x7SgDTv\cWnikjWb_ejjo_b^moXEHVRN  +     %   ##!" "#%)" +&!#     + +  +   +     $ #&,$1*0!3267->"3##2#4%!9%&4,&3()-(!*!####"?& ?Q1N".=!3;(69"6A->/#726)+H;6N+C+A3%+J/;/("&+&!-#$"   +    +   +        +       "!$  +#!* ("2&2,").%()#+ "%&'&!/.3$58"(=807?2=?>?T$;Q)6C!2B+8!!)"''#)&    + "'&/&%%"-$$#"#&"# -*y"UD{o]tI@m:sXGW)Rg-Z_VOF?'y;)rG{TOA7%2&/-&%3)51,0$#5+11"#00$54/)8 #- ok:WLc{\f^RtFn76H^;BdVHjfR{vbyrapLgEMf:ChOTzMjW`IQgQax`{n}qlssxhtramhb`   + !  "# ,&"/$$ #'#    ! $ '     . 0( +*2,'8)/+/3396D;1D#C3+E+)>. 7-!4-!.)",#!,#%#' G/DM'DS1;H+1J"E@%7N+*C/*/&;14C8?3:N5>I#)M000##*'&&     + + +         +    +      +        %# +"$()$%-+/-')6?9"J 54,5C@L+3J'=C&I.#7//8,??$3K%?#%!&  !   &"  #& %0#   !'$%(,'#%(!  +   + +  +     +  + + +    +   +  + +     + +  +    +     +  +      $%!) # +' +   +        +  +     +   +    + +  "0{oUqeu)-vO}k`?KbD9ZQ=[MoAadt~wwGx*G,;L|\.8/96122.$."0##5")64#8E",,%+5#)!*&,LjpkoCuNn}\~b-F2881=CG#7A.5+ag7Ih;P1+0ENE@W1-3,ER(&:&!"*+04uk.L]/pf1]Q$*>!)*#ZU ~w!MR"}x,XgA8SA&4<        +     +# "!9-HF?V?G`FD]@+;6/0$(&#".+>FCqz>:~"gX!)--$/2&6<,B5+<!!1//B+3#$#8#@D?V0'?7G[;JU3@E 2866 B@'4903<=>P&8;$%+ 0?;P?HYC`xPpTI`U@iIGbBMc0KYB:"8M!-E#)5264<B<#8K)B)3:2#=,+4+ 3*$+% (!+*""* B2ES2B[@1_9'D3$2))9)50-&&/03:)6=26E4-J)(<5''""$ +  +  +     +      + +                ")*#/%' '.*'# 4<>5F6?@H-J!)6#8.CSIJXDA%XW&=@ )=!.$*/1/2-"#1=,(-N+LHZI&+-&_I>inBqzT|q[ogJemCxfAb(+8,#:7'G<)0/%6)/BaL^=.G2 #6E&);8-B0+/&-A>8A")52C5,B`2CUKmb>dDJW$BS*1Tf#QD%"-;ZMka0G?%<2+@*H[     !    :3:)5J5I810$%'  .!9Y=_tEvsMtiKl97C"OQ:F0QQ-a_"]PK@!8@(2>$'! 1#/)%1,><3I8C#8E&?B*F?,L/.?8%*,*1(#(!%"%"$*5-ND.3W>8IG6E6!>"(($#8"(2; :>)8I*.F29'#"     +    +    +    +  +  +      & %-2)9!,4'5.299+>&&.'>'(9/28/,A+1>4EI%A^ JV+\[*Uh3=R(<  $"!"/%-!#).*+'   ' &+57(=!*0!&13-4;*>!') ! ! *#$!J C,,/:99> ;$'!! ) " %" +      (   + + + +  +  +    +    +  + + + +  +   + +  + + +  +  + +  + +   ! % &1''2  +  +  + ( + %    +  +    +               + &&*10!&( !7%3X5X[9at&:ZTQKL,5(',:    + %&0- "-&  )(*6-.--$"0."JC*UX0\Y6e_$@@;?76"6,M%1I//W'5HMQAo4YaEhqG]vTUpED]+5F$06.257"##+,*4/$=5,B47WA\q7L_AavNpQvDq>f{7\q'/?*:&%8*#,%#*1)2-7>,:0*)    +    +  +   + + +  +  +      + +   !&'-!) +, #$!!$).!$/!19&=-,7$,$"$$'3!!5,%,$*3!.32A;/?^BX(-H%#,!.# + +.) )!)*?K(H1#0+%"5*)A(90$1()"'      +  + +       + +  + + + +  +   +   + +    + +         +   +   !   +'3 28!+-%  + +    +        + + + +  +  +           +  +  $ /+,7-0#4;/5%/#.!'#-. $# $G ";/($EAQDe8RjdvNGh<-@PAnpQ|)]m*9[4CT4TV+WgDEc%\Q$8;(;<14I719!52$"-Qq$&0%)+"    + # .$!.;#20>7JO(1:"%.&/14033#!!#.,&.LL@apStZv\JU\2CK?=W):O<8MK@_U`ap`qhu^ToS8gXMu[UoUYsZfxQevHI`P`{eskjq^x^nT     % '%$%*  &$ + * +(# #6/. 0;.: 36&06%36%*8 (0$.& ,.(4#:+#58"!8!1&#",3/1.:-0'++ "; !,'"*#),)! 2)@ 70-1'  !($!/'0B(=&$6-&3.1/ '  + +       + + +  +  + +  +  +  +      +    +""'$&! !!%0)#$&,$( '%4:'H> KDI%($ *.(2",*13&*?0= :=A<RCRCN>\I"HPB8X4 _M 6Rj9?gHY?H-?G<6!PC%Rh%Lh9%S791!&8))#%"(*.$#-)2)=+)!4 ($&(%"(      +         +   + + +   + +   +     + +    +  +      +  +  + +  !%   " $13)2(2 *- ( +  +   +             + +       +      ?3$ !$()2,5#)3'4:.6B(!/!")&)),4843)9 #!8&!A.0FHQF?^[NVwtvOv)L3+<69@%%5(%0/%( +#"("' -0.; #,*!m-nZ_IkKi!,;*/06,:CLDEV<9G?',3$+Y5B4LV),:SDLB3OA4D13GD8S=8Va@ZFdFKh%6J''-$'*;6:4"")&S(6#+4#!     +61*)!#%'*'d_7nr:asH[c2Z[%p]1ţ,f!XMZQD:3,('II-db$AC-:?J4K_Lfo[lqUt_\n>W]AZW;XZ1CK''/.3G@bq@]p@Q^G>PI1@0%3!%&'#'"$5"*-!251F7,&);3-)$!-+#2&#)-/&"0!( /55#;$#!5 68'H&8,$(##'    +    + +  + +  +     +   +   +  +  +  +   + + &! "! !")#(&*-*B57E-<*366F<TK"@]0/W<"- $$$D#):,(+4;2 1F?;":]%EQ.Ll1rb2|/xDH5N>,7$"48$$)%"%$4$.)%$()"++$(&  #    +  +  + + +             +  + + +     + + + +    +    +       +"%$ $! !  +!,&+4#&4.2$3B#/   +  +  +     +  + +        <)>X4Ji%:(&**4"!0 $$'+.GE/34291/,- .3+,',$*% E2%JW;BI3BN@DQw6Jx.;M>;$(0!-.&+*!><SZEN+/).(, $KA5yO}GS/NHRLR@R=<#<@+1A817=ad1QjUdehr#NdEa%&&.0BI!RH35:=J;29#38<%<2(D',0-1:2/9&9AHN'! ##kj&Gl/<     + + &+E/@R ZM <>9Ifii_PqbmxOIabRGȵ:I\+nh/mp2}}MmHuy;AI:GRH]ldbzfU^RmN`r28='.5?6RbAbNHRKT_5BL!10$%%'#63>O*9F:?7; 0='0D!1;',/B:AO<3N8=Q<I5U`@^sS\yP\qCNb1*64@/XiE^kNfwGO[:=Y>OgEUsSlO[qKOkJBYFEZ:         %$$(',(-*%,&,$!) (!".+ ! + +  +( ++(++"%0!32$39'26.%;0,-'/.$30$17"(9$3*'&" =)*=;&;:/>+/"10 ./&$#" ' 3" 3>,6$$*   3 +; 6/*)#,&)" + +% !  + / 0      + +  +     + +    +  !  "!&(# $!)"+23FJ#HO43H%K< NQ#@U4-MJOAG..L.<>.5IP>67/5 %)2G0Xr08K.?%+&39B[/(68Ki-"-# %*"?W9BP[tgZuBN\BNp69I-4(*#)5E[q5,@-4?/;H!#+$05+ %78'*7'/@*T]*7E@,@B"70-* '/8@Nd'!'(&%<3 851:!-B$/    +  A5K>7FGH`j>%2=#'1#:&/>0%?6BV3FN+@N-CU:JYCLeJOiPauDYm+G\&U[?qNwM`pIW\2IS8QkD_jM^nQTr>Mb5;P95N;         !"!#"*!.)&2!&*(#&!',"0, 04%>!1"' +   % +(*.-).)5 +7$67#;>)1?)46$?9$0?);%$();*CA"9N,%=.+"*08"/( -*!%,9 ! 4 H4!9Q$:F%5=$-70,3163-943%*5&, !5 1;'5$-" & )- 7 +   + + "(    +  + +  +     + ( +"(,*74%9),0)"8&.$.L&(HT2PX;S[,=]9=NG5?>_:*Qw-LoIK`SAmHKeQdeJQd:*J"$1!0.!@8#-G (9#7? AH%:Q'bL0Yr5?r81K-0/I8!_V)`j-Ts;Xg.i~:h~LIGHD05e8%5*755R'<##!;(-C'6& 3%#+ 3(%$"#24 -*&   +      +  +  +  +  +   +  + +  +    + +  + + +   +   + + +     +*2   "   ' %*&")-"04#/! *:!*8+*7%&'14 ) +*.!(9), )    &5*;'27&:@,AG&@D.9    ' H& I_Yd[1: "  + &+8C*?V:'/@&&%/')&!AA6AI/3B/4A1CT'Z\9ck;Ha,9[/1=&+<<)*"!2*@R7G$*@B#79,-L(AG$&- * ++71 )#(6L9OhBLaMbwHFN.>J-:@+37'6CCS^N?QS4GfEbkZdyYwd\aNBBQ7AdPdtB]TDYahwB^[9SZJCUd`l[Zj^;\Z3PgEa8?@2,6*7F-1>.4F/G!4;/;">G&2<3:S<\f7LV1BP:CR7G^>JcHWoCLg9Pe?LdIHeQSrTaGhv4Oa@M_EiuGPiEJb=Wf4AM4>L1 +  + +  +  + + +  !# -,.$4$!)( &-',0*7(=($1(,$ + +  . )/0.+1 (6%.#7,$.F(*<0+7..1*,5(/2*5.'7/E.TGZ[,Ea5*O56-A%B&1.$$!& $JO2&0%$ GCF.N--<U(G[,D_EPLGPP,GZYf(Le,4=30<:13     2 se2ZL}J^l%-9"$ ++ =I!]V^Q5%kkQKBCDA55&*"%#+$5'2$+%"0.7)@O8BK/:J$(5&4H'7C:(8N1/A=154E524J<4M82H7782-?)$9&&+':)"->)62   + ) $8# +=?:I*!F"*#  + + +  + +  +  !$1++- #!+&?7<@SI2SY1@bE8VI@FAKS2"WB"K$ #&*&%"" +%)%! +      +  +       + +  7% UM 7? +%  +  + +    + +   2LIJX     +   74 +DLC?7WVNLHR8\A$ +   #-!%$)%*+$,&$(/&!*" #('+'!,7.(3&#-" ! !'/-,*8%/2"3=$7+*1-,67+?A4HI39E'"&    + +(1S\9\z2\}sxnlcbR'r|XR9//?K+5I*()"JJ.>'!!;G4EV6BK1CT=TaRTiLTk2Q;8ZrYbjLW\=ef%Wg"8NDK)1'&("3#M*Y4J9%25$,/($'*++1"/$$+#)8D,BQ-4A04H/P[_VxEjrCmGTJ(kX$qH&\9@'(""&!K9%,%4!-B'3:6*30515)(:5>Z&%($46ICI3Zu@MrRVvKATO*G?4NQYk/5A;F##3"&#$(0! &##*$OP+OS4=M6G[0CR*/9/-<,*:5,:G+AHFT67;I4SL%8kCws:dp[}Z=^j=[o'Bd$Aa5LqGc068(>PHFc]YxJv:gr8:K+8D0;$0=,105#9L0J_Jg|h}foT\>KZ4HW9[k,HY#?F32(/'23>"1=5?-.'1.)/@/);+MT&.7"2>*2B%"1*6G;BZ@Ng@av6Wh/IY9RgGJmNM}F[|8CO$(527M21KG<[CJ^>KSMVq[~Me}NI`==Q:/G4%    + +      '!)!$/) ( $")',,22 < '"   ! "+ !).&$3!#/) ,"*' 0)C2"HETH0=P+@C/UC,PX2OR=HX8M]E:[;;:AC"$|)%SS#*;J+%'0:@-#//*-"+E-ME3EP5?E/1@632:361B7)HT.HO9@L2AF)LL1IW)SHBU!V-)"!%)!+*&+"* +&#*(*$-3(511,   +   ! ! '7(@8BE#:%)5/DD.;I$HE.Nf9LW9QR>yc8Q1Td?Fd9:O0DI/6`Z9W=JVA,O607/&4&.6%)D=)-!&()*ED$PT )=<3!$F0'%&+'%"%$"    +  +  +   +        + + +  +  + + 6'LN8NU?XO)8C, +''2        &9#IQ + +    +  #+*1<6,.6&;2/6B+DG#BC&@:+dm6B>%sDE<!+*)!+&--(5#K'0WPV`JgtgeiHKo.Na/@N+09 (N:$+*)"&(;MN/OX31>@1>F)8C5>3UnEhyKJiHpYcM(?318F7SHH\.HN+KG(8G.+<<5,+4H.[W:-5&(3?2(04)KD'?)'31$hZ"fPK^Rv.(FJ E5Y$18hM6.7*=D-;PN\b>NG­:Rs#18.=-;(46",+8&*".'%2 ).,*D):B&IK23A73?B2_e9agVsD%9I77C@>X?4L3AR-IY"KT4C'>N)8G&8D$/?4:$. -4$:D/]p.\X:>)2>'0! //0G?JbDOkJYs5Xc:GaEVn@ObH?\:BP!1>$DK-IT_5i=+HK;(6P&&,)%"-"?&,:.)%%!+>(FB,7I8'6:2,390/CC,SI(Vc3Tl6OfAU[0Xg7Sd8Mf3>c;5M;4'&%:+8C&@!1 !.)*!E,BICDDCE&%*! '  $ =( 281  !""#'#"#'./)8%78(.K31'1$)9#+:;(5<.-*#3)+& &# ,.0"#)%?-"DK)=M>KOSC`N[L<4(&.;B83D6K><2RC5CGDE'QM5NmX]_BFf;3A*G:*AC$&C(9,".<;D,A&(?6':6);%%E260 (;!$*!/ !     +  +  +           +  + + +  + +   50OP=NTMc\EYy@WZ,DU&'M-1<   + + + +  %4CX*6?  +  + #-017,9''5*/8(>B 0+*!85 ?/PL)I]<^_=@J>`O/J3D4#F9 20%J8XW*2 *"(+/)"(#&$&((0++%2/- !*%%<",-18-7cT4Mc:]u[cyn^noDe{0[l->%D58M+&'.%-NiZ{:?"#%l0&,<2-?@J\;=P8H@)PN'Yb/0J &-,%FA)"66%E0\6!l>$Z2NG,ZmQMnBE\?K 9==A,5BD%NP4$7"&$)-/./:4*)%) %(2,9-9),615C CC!7E"9E+6&.!!)!!)&,A(3=-AL3_b0J_1/:+;E:Jb<\n3_p0V`1;A3A*EM7Ke=Sj%<;)&!,!)>0,@9,@=.KF\p54?6*ICW|=^d4L]'GO*GW2=T4S+0=%%3-( $(%        #!"! !"!#)1# + # +*;/23+6*-I.HLVR-[b2Kb;GU9HYAMULPR7NSF;ZD9LG:B>598c)IC:8UVDIj?[WHZh=Jl--2%0+''(3-(3+!&#)))$/46,%''!!.'* $.%03#=##1 /3A6 4K.2*,14.19 7A+ND5=Z4DJACN4CRA,A515&V6+T\1\h?AhJF:500.L+:939918,K:!OM+T^,+R;<8(QG)Q\;[eAZk?9\NCA>GS-GU:9L?H4!4[*"81$2-%%(&& !# ' !)              + +  +    *!67;9@EBFH6ETC>C=QE;P<),)$   +  +   %.+O:4%> 3.91 ';-(+! )!"*)J=EO3ZX0Lf?W_L3>F[dB]m@cz6^j8jx8;b,$!ZZHcp.G^8' +A:"91$02"%#!!)2/#.$&,(%!)$09#,?C/9BJIOKPS5US@E)$:+G\EQ*"0`M6`3Mg(.%0J0]uYb}jBZ>}lgxpijR~=Un1=F 1B@=BI$7=,.B%3C01;88?+6D*<'3&4:*AB8?VFB]LxL^o,?T!."X@(*E@4,6GP(7O(8E%-<'8"*$##/",14?)8;#Hf1S2$5)&3'(7.6E1Wq:]pRqGd~ChyP_OcwA^s0gi2r|7~BwB\q(4B*RZ.&TFQpIUm;]f.CM#2C(DV/M_)[^/PS3=NTDAT5;N')9.) -'"# $   +    "#  #$%'*  +$1/0 35!/;)19;77D IA7PU6OcDUX?Gb=MRAMT;AXB>UE7H:B;085/E$!24&)=i\9:N[*)27#"!&'( .$5*"$%.("2,0716"8?+>L.FRGQ^CC\68e=3OKHD;G\@CVACZV/@P?@L9.J358,/A4.#,*;0?@/D*#<4$27$0#.1"24$.8 (;$%8'.1&%/!)1)%4,( ,#'/)-&./.#$(A4&A#7.1@$B6/(J$ 2#'$&+'6D)&FC+-6?@+LN4:W4:E"SK Gm@;YIEIB5O20"&2'$UC)=XI@IC37"2F+IG5NNGBGFQ4KT8P]@LY;BP1 *49 "$,%  +      +         +    +  +  " :<+9*=;04C61;748V=(/&#*"3  + +    +  +.#16  )&.,),3+2& (%(87:=DNUH@SQSJ@7Q2FE?D[UWfY_cPOcX}I\iNeBM4ZXVdpLSu-7E-38F]9IZ1LT,LP"NM4A!#-", )$'330>%*&$,!2,$.=62<=(=5!#:*$!8O>g-4QF3:37A#6`>=XTOW;Qf*7X!L^6}Z\rLHDcnw~z]eRxG-at#BL2NP7LS0BJ-*:.%|~>B`>01"9FA.K3S{1@OsoDE;dD"2:;;?^aLnc_96<+0K70I+#,!'-1E> 6..3'1A$*-$"/4:FGEMWCG\iI]N9R<0AC:GAZ{LF\LD`@?V09N%9E+9A&29%$0*EV=>N* *"&/ #&,)3'/%.'3(.*"& ,#/'0#(''8D2CU/=P'-A'#5!"/#+'(30#1?#$)#$8%1''0'/!'/',74@WG]yI\yYSzOTrFPlOgMfLcT_UVW_Oh8ft6]i/*2)-59+@A4IHA^CL\.?G1DS:HXBXw8`v.NW4OX"43# -""0+P8_s%<:7,F9)%'/"$%  !$ '%"#'$$$(#*/*#!/'('%<7/JP5,U9.9;(6859.:A:*,9C" A1!'%#/",&+5* 6B$6L-7N8>;/@K)8E=24739..C/MB)LGAJSI-=[B;>60B63?=-?:2<.C5.ID-/C::8<>;0AI9CO?5MA8GC-E<)@5&>/&3+*5."*"!  +" + + +     +  +        + +       + + 3 06*9'9.,MG(T]8DS3=E0%>40>4(3+&"""    +     #10% .)" 1,/ +1'$00$!#%2.FT+OG.JMFCR73741YD2301A?DHL%1W<>6-VQm]\oQYa8PoEPFY]icSiDNJ(7=1;:4Q@FKASZFS^FGP-LR$>R*!)0' 5(.030B!-5,-3-JB&KGFLRP#NSTP5ZdCTO5IO#0-,<=>M5BJ.IH)+50->0064?:=+(51.C-V)):9*+.*A>0Ŕv{yZ=85-238.;QVAHN3?O6TBHL$/*+9>(3>,/ '+&)$$2)#!""'()&)(1(>;5EZ:IWJ8Q?7L0)2)/92)E=4H6.K0);('6"HP(EM'I`B9N9-@;2HAKlTIhMQpP[lEQeITkQb}Lo?G],*.!#.&$'*5/)>COm7IV,DT6FW>IbGXn:Pb:\m*KK68   0=Mk-KY .406N-8B/9#/#*(!0*)>%5H)1G+2A4;O6^o!IL#$" $#" '$)', ,  #  '& )*(+91 1= .;#69&=C*CB2K@AF=KJ8JU;>XD4J;481A(*Q*97+X*(H<+3O46O=6D88?35E4%=84.%3;59/=+#6%* %"!)*$0# . "$"+##*".1.%2$++."3+J>(JJ*5N*8.'&"-)&8.C=DOUO%JZ+@O)1O$.>-J?/9T./7!';!T6$Wd2/`>/35#8= "/30+"67'27)2 +50 / ) +/*>8 JAFa06#0$ #0/,*&-!'(!&,#1/#YHOO)0)%8/?IPF;C?5D@R\-Sh7WN#aT0@4.*?$ H/DQTOO]P>\P$2.D2TOWV8QK=44$9$ ")C;7R`HJWIL]AAL=EE12C".).9 & (1/!;B--4)C<'OT??ZASd=Q]@P^I^mBmuT]eEhi>Tf.=S3Y`HW=`j/Wc2HV*;A4OZ?\VPhf9vsjeqppty^NvMGM<7L7ES6:O3@U05H%+<(59!!-$:2#DIIO&^c,$85)401@&Wa':Y"*66BRXNI4F'#."&5*627IX7oX+kX4q7{{2705)00675@&,!#*!+&+"-5 %21$42G+26-99D+0#-1;F2@%9Q8:"%(!.% ,#1)4%3B*BI'1@$.'2(*&$ !(%2$,A(,;!6B(:I,1H5-B/'G,DR(2E3AR;?[DQlFoAjz=XkF^xPxAV^$(-(4&40,H;Zg1DW/=P4RiAZi?QgCZk7Yk,AO76  + +   %-?W:D^(AN->G+.9 9G)/%"+ *:';J$8H*0A,/@:6P=U`&,")&&'%" "'!%"  ))""#$!&&""!&%%)")( "   "##''.2%>$(1#/5$36';B,8F1.F89>08C1;E99F:>9<3>+H &[0">;/A./C/+B2IG)0,+$  +% &) !%'2*).#'!%& )*"++#0.'98$@F0KH1BX41WKB@ICR8=M-:E51B)%63.+0  #"%1& 4<&:CC5FC-@8-=- =/4332?!-=(6-*&&+"#)"#*) #%!.)+2-/*.!4-/999$#>+&*%A2$HI21H:C3%2@"C#&/*):7-@I35M?3>4%85-$$+,=2%4+.-S<3Y=!3H''#1 !( 71+9$19*2<(1932.-;!** %1/#"$9/0E'+8"..&$--'1+ ). ++& ")   +           +  + +    + +  + + +   +  +# '8!=(@)E81AR2NR?4L:'35&'!2:10/LDWa#@D"oW)d}5bk,Xf"`i(cg2_^,;ID64&&"'$),@J4J&*'0 (6"X,'2'$VX076)H,*(,+ISHScBGXB?P>KM95M//@&) ,9##0*.:;"UF27?/&@-&0@*7?46DQcO\lKNlLB2@S9/a|FJd:@D(4?LW4=OS9+ IF5MS3^n>}8aiPqvPjwoGWTX>UH_6;D#3!/7*VgRz\ba,'ZqFQvZ<'M>&."96$PLS]kFkQ`^:t{$u' rP 'pE*Nm9957""$,)#$("(## "($&0/<4:Z$?N/61<&+?(:C0WW5Uk8ux,mp.}u&Yd#AP(>H5=:?'@P,?T(5H(8L"+$'!! -!%5&?N(KT?H1>#/)    !(!*$,8"4A)(;$,*&*-(9,;I-Ym1Sh:KhENtMeItDJhP^LWn(>I$("%%0!6AKz?t6\d3L_AVh@]lEfzCg}5]l+RYB>/- 02 /.  %,%4D6;K*;N09C(9D)K_"LQ-4.8)5.;N/FO.H\)>D0,1@04>73:0%=/?%F-046;'6;**--;.#0!$+"# !#! #$*"!'#)-!,&&)+10%H?.CS6AW=1KK=A8+J7;=:AD;5G.1B58@+/A.)6)(12*%<11H%'A7(74"42,/!)&&*$5'!6''$',&' ,#"'$.#'&"+.$317C*4C.9A0A)@%6IEG.-E8,8(#7+4& 2;52<,%B04.-#5'"**(+#B(KM:W@28*6;-""% E5 =X*+B((0&M0&1R*'<%8!%$ ( ++(+1 *&# )#($"$ $         +  +    +   +-$'- +(+ +!*  +  +  +     + (&-.1(5859H=>B@;@,;A 4>)E<)^Q-_mDPhA6TD6T`>[M7\O^[Q\|T@VNdsHh}3Yo &%%)$*!3069,+ $<$!#.'".,)@).H=H\IV2HL,bXA^SrrWf,67)7A./C5.F6 5)'# <8CDC8(5A:;F?,A)(,##,Of.bw4Nh-E\ITg::G.gR&2gd&Wb5m%m`6]YDWcSNZkSeHI_]tPqkFAJHLE@T-=B @H-%2M4=J%*?LZ^c+60FWB_mH|P|PpF[}43D-6D)MT$27E W].\f:`z:Sp9Wo9J_FMkYh=L\>H]IQh3=I"/?%,66AF)SZCr[_If?]u;\mCXsNjSJXq:Pm2P`7H%JV!7>'*&'.?+4E7Yf1\_*V`0Vp:O^%1A =G 5=,)6- 7:/I:JV/;K:EW(9B&?D;9L75O@.@.:7#$1*/*">80.6-5/+6.321&@!$4$("'--5'6;05>&+;&'-#)-!,3"1$',$!++!"#"%'*&*(,/-(7/3+$!$$*3#14)0=.0' " ,! 6.$4'"+'+"%*-70951;./5F)5&!C4%2-%26@9&+  ! '%(5)7. 0%/0.<EGBB > 1 6033$)  +  +     +   + " =-034bW/de1]hCYob^nQSlE=-09-3?6->47MY`vFLPGY`_gwPVr*41$7@ .2'@ '#+(*; =<+HP3Hd=CWEKfT@M\FP]WT,eaPi[HZLhvmhylkax\It@XE;{S]8L6\xE{6Qo/;/\K&p{@f=lGBhI9_J2H;wp(el5WmvrFPi+FB-O\AO[LXYC_a:ee.efAzp;TwkfnYmxg5VZ&1?9%)5*B=0ZXDoLm}^n{pQx]eyXenW7_@3<(0A *3#*3),9#(3+'14786/EE:2\0@@;)!#0+@IDG1OjC=^8AM0ciAt.SpObpw\yKDONO}qMOsB^o>z>^rvZcb|tLb/IE7Tb8O[2?M"7A)`LJV9GCX;B+'7$""-4/:B,)40<3@dJhjF_aXoK`oUlkvNiV>O82A+DP8J_<=T" ,(&"! +,%0"'5&)<%BO'Yf-Yq2Gd=[tF~D\hLT~8e|9\hL\rDL_2Ga?F\9?G'8L%NT+cq9}}=s}K\tK8M@2QBd~GzETaMGa@=R>C_(2@'3D,)/  14&,=%2B;FfCWt>[rCXnBiy-NW0L5/F5F(C.D)D)E*F+E,C,F*J-H0H/F/=%A$>&q<g0 X.G+6%.'! " +  + + + "#$!(' #%# '&/226(*4&&-)$3(+)).$% 57 -!  " #  36/39#&5"(30"$'%'/#5().!7*(;+/)&60"67!0C+-%* $%13%%9!#1&3' ('+''%#&,(-"+.(1>'#?+.!%,'&*(27/8"!2"$!'!("# 4AG2.8-)4+#:/B!jZ;jsVVvTMoXCBOIKX9N=#%+/+#+REJNpBCTAjN^Ap^(@'$1-$=:06>*)21)/" 2,54 ;O--N=/B;6I.2ND&7,+32VVASoK@GAY]^n|jryynhhxlZpb^kRFxFE{b.6T>OQZXA[xpuuQkl0`^E`obk{f>]/JD.;1`P*WZQ}\Qn[x6R~/KEEwWMGQ:\a=LtPedQq~WynY_cnstrf]pQIUYA=".8"(%-//9!,'8;/FM0.670?1*3;2~Mj|SjdpQdAPuU8NUH\rP3M<83+II.hE2Q=GZcNe306+,8&+!( /:$(0F8$#<3+/<%,I+e&9^*I/K<;:F0D4/;+:?,=J55A&WY.LM+=6B$CS%EG/1F=;]BBaAH`EGgEfx5@M'+/$%$189R=N]3H\3KEEKMDJCKEIBMCJFCDKHIHOLSLNEXGNKPIMAE8H:E3C+n;,Y8$N1@/1*&%&$$!    +    #!!('"!" #&"$'$'% )%'(!-//"364!+$'  #'"'"* "#" "!".).4(/7(+3(-8!/60618/2$/-%50$#)# $!"'"!%!8#A&$ % %*/$("*1#1%  # /"-6$)3,&0*($"!!&"&"!(;)D<>K$BI72D)F=''M6,6"=1$<':)&C$),&'!$&"%).2)(%7%+ ) "!      !%   +  3&-+*+ ;+GA*2E19'  %  +  + +   -1*-2$12M7IHXOKP%!;%)     <-&.<*#1-*,#+7+-"CHDKb^MX^WZJci9]l8irG^l$/2/8@EUhVxMH`Kp@;bT!`^1L/2/fX+CD9aKusVvZUPGm|.@M,YQ5uQd9K`J]qKlFer^hah|qjpdpyFT\GQLGIDLK2FA1HE<:&UOGG>8WRA_dMj^H"7&/$"*%13,8'-; )8$-;%0"'+4"*3A;+4T5)2%55*+B*=G'Ub.GmG'C8(24kkPA[;s|\wyS|a`rrjctnB{=YS`L3?M/0%08(56707B0<,66BDH7DP&GS+r >C,fb$@K)?961(+),,5.,,036E0/0&53%GH-EU)SG-EF2lg4DM25+'%+*DC;:$.?/4@(:T=et/5YYLrWsRf}DlIcW~R{[tNkK_zER`~S|ZKr71H%$8"(')&. '$!+$?F6JY1AV-%2(('/%HP:@!, '"*%!  $.AL-@K-@J/>J*BP)EM0DK,EO*BS,AW/BU2GT0GV1IZ2C\/JZ-MZ1Ef3Bd5@a4B^6Dg5Cb69^44^3.J/*2)),'$$!     +#!)&$"!   (&%('(&'+,#/ +4/#(%  "*)%0#.$'$, *#&*#&.&- ,/*&*4#%4#31"5<'=#*2$0-+50&#!$  (',:(!9'! !$!!-$   ")*'#  &!+ $/    #7-/5=5&8640'D>89C!N:@D63>%     %   + +8 3  $D/NQ;W7FDA?-   +  + +    >#$JB*DT-GU=OVDSYJSaA-D ).'   +   #%(;.$5))($,)*1**:&,493-P9KQD=EU6QO,NP0AC`(!:(32R61ABA*9"?8&2<#10!\[.0@4  S83lmZa}aDj:%8!-49+86(7)D>8I!$2<'=k;Wa_uR>a;MbN_qff|i^XN{AQR;KQF5X2.; )5+076=LZlsBmn5Msd*2%9+tKs9W[<:gRicdnxRjGV\[jF3N@EI^PiPpAhzN|ycxg~nztjsYkkSXLBH?52<9%2#'GH?O**:)-4"'0)9=#&0$@)5/>+gRCnVVuk]mƼPПWc}MRhTUcw_efuYJa;LJ)@@&B@K7"?C 87'/9'KL2}.Lke?9l(Z|9`/Og ;H$,/6>"/?&&--N6>B(,7,A7.o_0VY0K]CVZ*J=K?',# 16 , ".&-=')53G_O>XSZ`|dc}KuM~Q{I{OkOmOpOgUPS^Qd]F"]W"*18DE.;-5TU$ho4E^0?M%&0*-RP_]*8V,5,7*)4%,!+  #  #*4AQoSWoSVrAXzGYq/7*>QB[}C\i*S[DK8@26,:K9BQ@J]L, /$.%-&/)3'2-4,4);'5,:,:-7)6,=1>-D/D-D$/B%0E&/D4; 5H 4H#4<'3.!',""'$')%    ($.!*!)   ! ##"'((,'4,&#'(- +$ *,061&D"*-+34*9/132'<-3*. $/ &, +6 15*? 5."& %/ 0)+"(/$-',+/-9"9-1)  ""% #'#  +  !   5$+3&   !'1-0)"6A&(=0-.*.4+1"& C%?L?L+I=+;W@A; & $ $$ " +  +  (;-)   '&+!!,;'B-.#  #%1$ ;<0D<4?&/-!D9      2 +=- +F8>H 7A1)(  )2-H8 DA0 <@JB%LHMKG?SE=G@H4  + +  ++8,$( # '%5&'8($2*! (2-.(,?250,".2XW>.I(J?'9J-*;$0A4CI;.#1B!(Z4A$(4-#=,!+ &45:!bb1t)njEM)DQE=ED)@12@D[rI;LQ|^ESt>7T@JY:2Q-++<80E%9BC_`_\PBV&|U#Tu/O%1@MGM`pklknG~gz{=as/npSYmJ`SSpkhqznvqcc]BQFIE97A)'/-2324A9<;$=J*5A81D2HM1PZDFdD;H$/ & &+0&)/!,"(A:81KP42I.+;*"0< ,:+>; !05.`JUw{xpadN[q]Us[{jbyvO>T?LX<[lNZoRpwcbMNrB?nAN|;SDp-ai$:@&v?k{qYYu]|8Cb?GbIOo3=[0'G>A*6?!-+!6E/CI$23bE2_L4BF9JQ1@B4PF$73"99+&-19 *3-";I6_q3vFsXyNS[_aQWuM^L|i~ygn}^[2!gs(p+dl#Vc-k{DmNuLf{L]~_hWmSNYnfuftrrqxqxd[_7_hU~fQfXEWF)GIK,9I44B;KK;=S,-=",   #" #"5,19BLR7QG9Qd6:K"6 NDX[(Ta.!@ ""34+;,.$"!'$  9(5"5-9'?=5MX5=F$2G4%:!(2=I*1' J>&3*""4):5Ef/KU2GD-E:1>+$=$69C::HN.>RSIG98=B5=G>FVEORH]*=BYSxwt`Cyg:gT}AYTkL-P*"8&0".((#)):5)2<;&2*)45'O8;>73HC&G:(43%57*\>J<?4JB&M^+-=3/*41_uC{QX~JFcR_ginp|i{ZlIWyGBkYTqi{udoL^}_YNo[WJ\IyRB~?Qo>:H(@N0}>Sm5,C8&&#'9$-#%5@(2= " %-/DR8Wl:XbT`zLZzJIs`Qt[QrJ?\_a[a^jhjhdJPugqivRBdA4@2.8;/AME]EGZ0HQ.TZ(DE5LXDHhJSmQLiKcyA_fCfr6kn.QW,;G8E]*5K+9J48H:AM:EUCH^6M^=G]8M_22C<2FI!    + +      +  + + ' +,)-,54.-")'&%-(&!* )"$) $ $$$ %#  "#"#"#!"&#! "$##*4%;1'0& '$+!%% !$(&*!"2 +/+91<((@2+2(,7")2-+2$*509.<.8*= #3)"/!-%" #&%&'&/#"0$("&0!+ +*$#  "&$&0-&      +     +&#% ,(.6'9:(/E3*7(& + &%&,#$  !' $$ " +/3, 6@)57%8 -&+ ",$ ,#3/!#.  +   + E:SR*_MDOIL62D>52C6+#F4DE 9:-2   + +'')22+,7#A> -E6->J?6E3+7#$ * =\V2XcG`cIEaA?Y(=J!MP`c6bkKKoUIU>';$:* !&!,  +@-&*=&.L^CMV05G.#=$(01/$#   '/ A.*0#5+ $"(""/'.'.?@#8J&BB2?S/DS2NM"eY,ib!6I#.kW[@>9JJN=i471C=a^;X-4F"9>;LgN7S88F*cUdFkP~zN\zj^baauq@-GK/:uniWoVtv@3::?8 1"70%BN,M^3GQ*/5'23(3=69W8!90 !)-$-',+;  !=F"SX(^@3>a-JH!'4$*-(>60C>%SR.=8&sL)bterjMgmT|`p\RV}I]`H_cW]vmiYq[hIBvx>pwC`iAjd//vv*\j'a]-9AXJe:z.qRkhlZmV`N_z83Q++C-6LEm]msYRkCB_E`N=zFzO`[W}Dl.P\$/G6KlOWw+8F#-&#,+'+ +/D     $/E5.A51GA@YI1KB(=@*<<7K5.COJ`X\}^\{ejeoSc|JTyH^sN[|SMuGHX-GR)H\-6:>=HPXpE_oAWu@s4Sa9OgBD\HKe\b~enbeYe}>]}7n5Ur1Wc5dt2wo2J]7QfX:AR50:;"   +         " )),0143#,3",( .' .(#%'%( ! %%$ & '$" !'$'"% "'!&##! !%#"(!."'"'%#+(7-E$82#4(&,#4!- )$!'%"$$'#$ .&-&"0($/,58:!@> 9H$+D(3=/?"%>' 4&,#*#!$%'"(#++&*(!$ /% %1*$ %" % )      +       )&-0 : $()'4) .&-  ! 2* .@'3#,'-" +! 1.036F7D,4=09?/AF0/A4)%4SJ#KY-E8-4 +$   " "/+$8993G@DH6+@78C?-$G/#>S/%@6??04I/:$  5B1 >>6<$/5"3("&-3 ,:=<:<5;/8<1B$, +$ 5GH?B]PUSI]iQXfIZe;\l>]_M_mV8g[!B>/76%+#! $$3.<*(!!>;92/J@<=JG=O23 76! *  + 0&$5'($'&)!(*/%,3367FWP_[e~hCk3>M/4J'4-E-FS83TH>D"39,8E:9Y$5:'PK75D80-)4DLK:kyaibhfF\XmllDb#4<$GXJ3:a]zgHu07D1./ <>-@@72=B2A2+1**+=&.@0('"-2X1L= 9A*"#?1'DL;+T/>I.<$}+g3?Z4I]1\O,|yVypWFbsjhBBGpS]QXeZ`lkLlCl|O^mPNmE:V_QMKfoLo}~UvSoG[snlWoDhlJ{(z=suu}zTy`*@/3;!)*PC26,.;;DKb`.LP81.Sk8ON7dVPTu\YyyH]h%P>+/%F2&E@(58$8G!)/(0:'9D*O^,L_-Jc+MjHgLUiUTrMLC4BR+>#40?PVXqL8SBjBu0^vS\ZyJXu]hX|H=\(#1$"3:0S:N`!RZ+2,6"(%0 ' % '!#*7PL;F.<)$++.,&(/#'1$/=)6@6QjCgGO]PGl\o]wJsTtEg~Bk|6F\1I^9R`-4:2ENGYqVeDNb[mRm>lIxDexT\xhi^lXkJIoDcD{BfHc~93E?DZAL\0:C2'6,)/&'+00"&$( !''$!%)%,4157:-B"5:"9= 3D(+D7+;6=9"'H%&;2(1"$7$.!*#+#-%* 1"/ -*)$4#,$'#-"( &-,)3$76 ,+) +$$#  " *..*$- &  $ ! """##"%&'&#(!&!! '!$%#,"  '4%36:856$*22%)4-*(66!3')**)*7-6B1E/DC59X38N6/I96775?77E$$:)+ $5:B=> !/,*! +',(+0-33:-.&) %&$-%#$&$(./"$" .) 6 L4IS+3B68@5=K+';&%!3)49$-@$9A28E+'51+0%)*,C"BQ;L5;C8%LC=J_7PeL>TQN_OBD1@/+1/5>8"3/(*AO(UT1rk@nf>TY'N_2`<.W^!9jADO-4H2:LG,:?PLeffdIvwjVnoRz%VkBug"rGSU>PQ0>K8B-;>-99'/7#//!6I5>"6$#!1!kE z9>BS$J5rr"Zc&24&[H.~~pvp`rJpt0Ug6dsFy_Q^_VPCokXWXx9\gNSiHHVUZvQ`^CMRc@[x9nGDiM^uNDaWGaTVaNbCm}p~TɛLGWc_DfKNk>UfV\Bbj#WO IGA5(B5]!9L#y$&4!",$"/-GX2Fb,4E3:=A78+1.1')% !&#/5> P\<`5Ff0Kb.3#&04$,*2$-"+#61?ZKXuKTl:NZ_cbDcNHtbuUnIn9Qk1Mi*?&-<&(7%.070:!'84305??$;OA*D,-<3.0(*5*36-.@-(1!-*%>< !A)!$:,3:":)6-8*4'#)!%+3.8.5:4A?K!*; 37"(* "& -" +E::H-9CI3G=/='8>,490+(/9-9*#3%'2"462.D'50". )0!+1 ;E4:N0K>-EI6;H8'==EBD2!+>.3<3:D2/a'#"7,)D"'0;O0,"]9!+#-8%'/0 /18#@D&.)O=TY1f[Ev]k~WfyPNYOm}:]lM^jBXJ6:Y*ma*HR.FO1MQ:T^H]ddolt^u{ppqF}YjO=.?*9O%49&&1)6*>.#CA->/#"0,#~,Y†UY!c>rtGp"fa6heR^vv?OpnYmOwHF`Qd{Lm|?MO6N]]urcgzG]sO7SNNPJ-4= CH/ko2`x2:R5*<2>N5#6)1#2?,-@?VdT_{C"D2(10DZKZmSDfE!E#*!(&5,8P>LiLZwHQc>@W5?R=>X\@bZZ|,"0'#"7D1F],GY=H![e,sy%no(J^&Q\*Jk CR"'+-*/.68E:M#[k6mB`u=.K7FaB,GS5C7-DTMn__N\zD[z/I_>>WHeErCMiNVtWNlQIfSY\\pEKcT\{I[zQ\|O\yWW~a[Lau18JQQd|Pb|D[v62@6>RFZo27B.*8;!7B+CD!!"  +  + +    + " "% +#$ "#$' (%##)%' #)((&$$$#'$&')&+$*")%0 ,$'+(( ## " )-"**(*,,*2+. 8/"=6!F,M+ 8. :#6)5%6%+&%"##"#!$ /"027>-A$-8$05!/4 )7))4!'8!'/ 0 ('/#!"!%,' ),/+3'3&+*1--7-6!/*/./-.19("+")1-&1!2), "/2(/<*6(0/- 2 #"!(&*! %$.%-0!+1*(3)(2#-*%6 -!%,(%"  0 ?#   ")#%.(&#-9&->,/;%+<'*93(8.-4"2;!1;!4<6D/-G-!3$+5+/76+:4<4$*U(.+#'%0(&-7)787H/="52,,5.3!0;$4.#-'$,"!**5#==E?-E+'91-1,=/13?3E8'7C/8;+0 +*;'7<72H2;-$3&/8(+2.&+'%1-!("#*%'% 2&04@0@@)OD!=K!D<$6K0/4,*@;(56115'&=85)933718>27H==K:,'!",4*-+)@8,L;@*"(-05/;-4?04U1":'35/860=6'4Q,)A.$& % +2'6>')?3+/"9<2,3')L+(! ! +&%$Q?!Ht:($'4-A;J8vXr$C`-&3*/!)2)$, 7*FP5mn@x~PMnm^V`=l@WrHIYWciSYoBJW?htPz{A\l7\f5IcB[VAwh{wf]Iym[j;lnv{ok1R`4-$#'8/ G0$]B$=7I2Գjʐ[ofys_o?C9EVV~qcsb_{RaGja_qJd{`lhnhbzTväsJ^wHYu?[oZldWkEww]_OEbW8JR7hR0H,:FCqpaGeRCvҔs}|_CAgEIdB\[59=:9B:c\>UgEQjHd{fJdžo_JH1A>S/?5)I3'^F8KQD8B-8$6HEEb5%&:C!$5>@Y=-*#$1/;/;S]1]qEb<4A5FA=RY-?W+!:#") &*&,83!/-'% # !,+"$/'5!@$/E-@:E1292*:;9E=2<09B0EM)/B"*($(/?24F-6<.-D+*,'2,0.31;-*/-?%: #$)5(#"!!09'*.&0:)2%,=!D5 ,*$%B0#.Z83$)# 4(%8=7 ((+;-%#+"8*#E9";*(#+GG)F[O+ "%&$cy~SH>JQTXMO]4Q^Oa__fV~SQZ}HEj\UNXwlf(29'.J;++WDDxdeE,>'")&"&%6%'A:"`83q5^{CXGF^*UW3GYL{]gdLLnLduVLhJo@{V{jEqiUdMPĨovt^JCc{Iv^sGbvETdPe2D^3JS-#4dֈ؀~rzfe|{wupMMI-LQ:<]>=T,.C/DM>:RB-D@/6FβGw6km -'I8.ES/ED2Pj815.9I7BF%9@#5D&ZT(-409-8(-5+)+"/!"/$*""%'+ ,#'7\_2C~PhGzE;*Li![[-mu,hl(}z-[i3[h+CN&7G)=M3Xk4E\59Y9*' &*!"%$449S)+)/":K0BO2?Q1GcGavL6Zk?QbOZ|CNo9LV&':,?@'($&&     +  ! ( #*%*#'#""!!%'#"&)*## !'&! "!  "%$&0%++-'#*'"($$ #&!%&"*')'-/0,&10#73%e/$^9#P=(M0'>-$8$0(-$#()!%!"   "!',*,.03$-0!30$55)+4../!,1$+.&1 7-",5"-6&57$0: ,8$(9 *1$5-7408/446!552;!28*25&45$49 76#)6#.0,5=<$4>'09%>4$9;">2#2="?=*73!91-C00*-8)979F$2A,#0'&''"#$3 -3/343**( ")!0/;)39130() /# +(!+&#'#!&%# *&3*&'&"#/0$+"#')!#'%()&*& '& ))* "2)" )%0%2D*"2'"-'2"6&&'**04:/@3C'8I)"5"%0+("! 6*&=-&>I@:J@';/.)#-!.'"'&';*8(11,*5'07-3&$"'=6+NN JZ!+L',6!I6/>U5OW5W_.KU dc)^f56ut)qp.^\0AQ0Ja"6E(4H8ZnDiu.Hb9kAn>Mg8SxC=^QNlVP{jYqo?au"+4A :C$'#%") ':+GV'*%3E22M//H2EaKaGar?HiHQuURe@MiG[8Kf1HG29E:`i;]y:tz1Ud&4<-5B($)#+'  +  +  +  +"$ .+(%%#!&%'%&&$(%  %"%#%  ! "(%*"&!% &$"!$$&"($($((*+-/+%=4#J4#^1$T@ W60C2"=-$:&*')"(##$##+,*-+ '%')*$ +$!-'"2-.*,(#'"'*#,+(,0.5$"1'*'),732;!,9!(1!&0#)0 1-#,/#20(2',3+3!24"00,247"4=)5>);268.8#31$4*/3$-0*7")4&32"/8+8%,4130)/1$-)50;:/=,20-!1, 5462)4-3:4,B%,0)*1$/2'3(+',%$" %'*$',''.0+4+0!+$*,.56.!)1(%$!/)&,2&3"./%:;:94?%E!-016*4AD#6K(#8.9"*:/8"=C2G#.=%,.2+9*(9,04 *?!8$$1' &D64CB7?;6E))?$:"*+&$  #(#--#@C/:@8:$:N6R(3H4,GBAW800.<"*;8)*+#3+301+53!0)  #'' '&&2&'"+,0"")2  4,') + /)80*$-)%+$76/4('1',:'! %$./AD/:P+!3-).)'13N2.=N&DG2aQ/fqjt\S8{s/82HEia3ĉUeERm08<4HJPw3;!00QI}Sj>rufz}xRkour{ryr}UdVXzomhqXvjffQ\LbOknIrbK.hr4|MzOqm~rh^kMoXp`F`Fos77Mhp75G.IT)+;%3&+&!$ 0)(brL_QNsX~fzTYuea^[hCnE}=vrf6Xb;|BIXFYMD9jr9EO)2J 4G/LfE|6KnE}OxAWq6D[7=V5B\?KjCE`WEkqwXv## (, 1>:D*-;!'" %,)@Y':?!.8,,$1KHKkIM`97R43LLG[@F`@NhSvjl]YnJUiCJYLU{Hm=etP):N.5+/&+2$8A-:E8)B)0#"'&"7+ AAFM0:B45F+6)(A9&)5 %'1%%*:#4B/*3)@5@J#0A#&3*'.#&% ) 5=2; +=%#3$&$%% # 5& 1/2;#16(-4)%&!#*!)#%.%7 11#(#-,76HB%bO/GM@H^RhnUXs4QV+LO44G$WK.B/;%,>*,4(R?*\v[tx_7WllgZI}*) '%!'&' -$'$+3uR/HzetZzTmfr\2xHYV`5kg"KW")$13:/M<ab,{z=Vt}f_g~dvjc~JuKmwLQro9iwK<6g@vM|vS_`dMmV{\ipDJ;X1|1do>qrƩ_UDeþuWhrP@YI$65;0xjV+6N7I`|fzV=V!/5,Lf#`p#4F)*/"&/+%*,-"%"&" B9-0./"<@&LH#=A-XV'CZ+N`5cw JN("4 $0:SjNxV}XbQ>l:ez4Ph4Mi29Q4/@D0Q\];NS(1.'%#+$+.,&5H*:M%6> '/$4$4.!9?1H/(5$%51,F:9I8ChKIocMrqUnibm@DU34I?GXNRyJbQPQFy{Q(1E '&"&## +  +  $ $  $'" $#  #,.#*.**0%+,+/'))/+/"(0(1%*($'('(#3%<-Z, T7G='/3)*&!'-.(,!9/(+ *$ #!#)/ -&#  $%+&***(.$,+*,)" +#.'-+($(%) -"% !(.$ +&$/&.')%'(*%**-%!0(2/--3/5* *)+$3%4(3)"1)9- =,;.;2333$',&(+&1573$0/0-)/&'!,#+*.+,%2/&4-#506,.05'6':,1,/(3*+/+#.,+*;*"4.:-<62886!14&5=)<9"1;/:88-.E)25&45+7G&-F&8: &5/(12'7>*8I--C-302*C&341K>:(9B /P .8$#@1$11:-4?-+<,;229-!9('9529:.8-5E*4B&,A0(?9+88#2)*/%9=9;K/:F(5E5,)2$!(6!3(+)'1<&8>$:?:/<.#-$    ' 4,$4+(*%#+$0("!%8.JETR#LZ-9E,)5"//" '.6$&,$=$ .,9%2$B(+I0DI?O{@2L<=XL;b\YC=9P1@F 5<8P^;)G&>5(>H!RH-XwQr`zwsueUNZInujz8S%0#% &)&,W@ywFw`NT[M]GRH21.78[K;d|gfdUCrm{pWtgx|synfWe~|p'9DrSvokqw]_ve\KJ\NgUqrh_yFsOe^PoxJxsvSs?CoĂ_wYtsfü{b_Xi~myhq`J}rzΙtF+I%.-2M&*8@0:-4'.6<7?52B&*1&Rc0.;02},;C-4H2 0(&396SFbm'*2,&(4DTj`:O! . &&% $!IN.$b^>8-+A:A=byQe`SrYoSWPoQl2{{7oq6ei[xW^qKiyXusmdhT[qBi|[ukne[;HHtaM?WN>\^eCan2N^:@]BRo]l}uK~}OxpF\]R]zCPh=Ur;Ri4X`.5C+-/*&#,(  ( +%#$ +  +!"!!%)-' .2$"""  ! "'&$$"*%'&*)-/-0,7!.3826;3<&4>&-;(3!/3+4+-(.()"*#(E&J/=@/:.,4""/!"+((,-)%%-"!#(4!7'B-+0($(-#&% +'+*),%+ ,&-**&*+1)/-*)),*'3(&# !)!#$&&*#*&*'((+%)%'#,%'/ +) 0)...)7(2..-#4*4-1)*'!0)#+(%+'."'&&#$%,%%-,$1%0/!/( 0'/,-+/*6%2,0,1),%1&$:.-- +"-5%('*#5&1-0(5(7-66+;#-1/4)5 01!61#22%,<>=)3B+:423 2->6'1E @7!8D7;7D0/D7=<'BN"&18)/75):*2%  /$7B A=BN!5?8/.31*,;9AD"ELJHf|C`;y,I\AAb}[jLsIUnHiFPlEVuLLzaSNG~JYotWw_@QGTtPgQp1OhPkLazQ_s{l\egLpLyWzaydDUl0&&%&*!'$-$*()!%&#()2>+,6"&*+ ')+ &%  -'&>5:O/8L36IA;bREi@:TAWdDk}Umo`}]]rirYixLDWF8TLQhCUpAQh5Rh2HY-\6&U5#-;" -) + "W B)*% .+ )%#)#-&-,,"&!/&+%'*-(-'(%&$(+'0*(--././-./3,0,462%892>$5;'5>'08%.6$/920//&/"( # .>-4;*:#'2, '$% !#&$)!-*!)#,'- *&0#;(A-\3RD'66.56!(6!&'*)2+.1)2!102//.,44-(2$0-#0,!*+2,+,-*$))&*'*+,++*%(.%/)/#.&,,#))3'-/!((!,'-) .(!-%/%".)+&2%0,/-0*4-/(!-#0$&#$0%#4)".&5(,%3(3(<(#5,$6+/(*)*'6)%+,0$1-.'(),"..8)5'5*7(!53#3->-41263/ &1/33-!40(7:#6:#B4D6!6M(69#:E)CD(;S,AE-;H&4M*2G62>(><4B *A 2<+;8)6E"9J"3E5?!(3 -))%*".9$L&6+,E@:;F UQ*J+>+6RAR06Q60J?@G(-;F:?N?PCQ&%N(I6()B%1#.2()1$6#87%/>*672+9=*/+%7&"+$.(.2+28%2@,)2*&2"%!   K3HT;N->P5CI:?F1NR0?S4 =1,("0&),0,),6-(6(08&&040#?>20=)$&0'-47$=M#B)"$2%) 2.F+)ZR#vCo C33KG8B=#5GI8*.&.#+$6A06C&/.&.9(+ -*)*U;mDd'ZL,1JJXLp{Dyy]f + + & vL L_vz.eqz|On;}oRt[v^gYc:chup}SvQgzbVj^KilzbHGBhx_{f|ni]fbv50\UwiwTkl0GY-K;#-!1FBMhyyWhJRdnogBUpł_2Tm7UpSrL/U.AJQKi|¼cZ}5XY@I>2D-58PX&@O'% %&;4+5 '2,<@)*//J806%4>".6 (MtTl)~jgÏ|Q)- $3!+@&8(!!."202=5B5`s0,DM+GY(FM*A3bqUkxUUtJ|PBTahq]MjzJ;RD@T.%5(-8;HmJp1;U>_xEiy<`KtZFpSJjNKgKT~^j[s`e}Yu+GP..$'.89C9F&Xj!BQ BK#0*!.)4$2A*%0CVLI@>/-$%-+8%# &/C&.=%0>+6M;@N8Vc>ZwWtXsV[nXDQX0@T;P\HZNFS;*=54A=8YFXrGl{@amBgx?M,.I./,.8 +  B ?!""%& 4 //(*)(/&1++/!*( *+3((.)+#,'!.(2*0(3+1+,0/-+/.1 ,0&+/%- -)224779%7:$0=(33';2#B3?5%14#"1$) $$*-.-7)1$$+$$(&&('###&"/#-$.+3--/%, $),-+'8+J4"EA4Z6;V>-?A*55 534044'- 4/!*6",."11.2!&, *-/.*--./1-/0+*1+%+*10-.,./,0-.40-/,2+0.1/*(.)).-*.&-&2$%'92','!/)2& /)3*0&-%3).&("1! 2&$5%0),("/$!+."++!,%!.((('))* &()'#&1**,2&7,2. 4- 2.7-";-!93:/!4/6,)*8&D1/33-!22"66:8'/9*9<$0A(-@,38/JG+EX)2H-8<%:A!6D"0H':;!8:GB$CN%9K'@S*JS(EJ)?PDHBP%BJ$>I&:G2K66>6.J449>B:?H.Z,3A51<<6@5?=/?G.H1IC6>D3.D6--93J).4%:8BB=7..!17'36?="E;$ED2% -5>/X,/,-31 "'"($$01Ld)S398>8I*JN;f}Sz5:[4c[0WtKtA#r7& "% +!( 3-9<.++()473-0%B@I3>H-:() &j4Jm~k}mcui{a{o:`O`]8ZeOoBRafZgCXkP~iwefSBxa{l^;r[r\TuQYkfjju|k}E[fmep?(h $ 1ss-_hDLpkBH;x~/ڶeٶwsXni=j !#16(>*0%D`9ǔڢo818:)6>.2B -!1<21%$9*%)*+&+>(/J\4dy&7I#DM*FO,fd!xgeVk{{D\SyРX/&qv'FU&*5<:H#T\FP0J!EE9XqIJU5Sa#KT&080EJ*6>*ds:h*hh HB.0754BBUiE?YDDZAShIUwCxyЋw]z_nnx6xx(T.>_DNd7N`'3B-:-8"/="3;$1A//&A^I\|4Tj(5F% # $ '9%!1!,<)8P5Wr9GN)<@+::0/[0<]>'H>.W>%@D!17(24 /5 23 05".5 )/#00+3..1- 22-2$92#27#51#12 56!09!43"3198!5;"68#5:3;0; 4732#/9 48!86!3:.7 14 (43'*5%#,#+*/*.-0(/.1' //.)!(%$+"'!"&'%# " & (' .(1( ..-+ ,(/*0.&-!(!0+ .22/!7.!8/%3-&=-"83"9/!3-"6&A314(70.03+707-$D97:#<3%7/-4;53=@2-;%21 .9'.9)79$9>'7D-/G28B1>E%EK1>N5=H62.4#AA#8;&Cj.6S84F4/C)16<08+G>-6J-2I798 3@%->!3:'7>,Q1#7)#,3#$)1&7+$,((!     ')1)#>B'=N6/#$!B3+0)"-31 &%-3;--E:.7#69#4='4>-?;*%<5:-*3X8UH))tI})42LS*2S:>'7f#=Kux[CWCALOC`Odq[lrAZmFtAA[{Oh}kEqoy{ʇx{s~am[Ut|ic?j:?S;`mEzsoUAU6P*5C'73/:/0&%!!&*72$IE5?%.F=SgMg.v9Gi74RT-KcSwhidwyQFH]It|ns0PS9LH&8+3>.5''1*5C=9O&16,IX9\x5*8+7U,>M';F$IU+WQ/WkQKgEXmElEcl0:=1,G62?,5>6Te@euGPxf2TW6Kd7Y[Ek\^Cau^dȄ^1=S!.4,2.Hd_{BSq01A(9E'$0(>&>Z$09&+:)$2& +1-?<#8JMfF'<,*'!/%2@ -6#BS7_t3~:XlDKf02D&3D'@K':L'>M$08'.6$.2=7;96'A5$?9(78",8&45$0319 /8"6/69!6:&69&;.":6#58%5=&7<&15*0:!>7'>>)7G(0>%37'3;%9>%9D$:@*0?&26%1>%18)4>(26'9769$39'8:/6-,384>7952#6: 4;!67!0:!25!/4"445= 0:!&/,0.05/ 17#14!+4#6+20 2* 1* 6*,'7*++ -#.&6,!9-8- @5$=8$93';4 94";7%230-"7&9'++3%"=1'@,"@5 29"*17,%62!A3(,'*!,( +     @,Q!EPad@a?1P3-<1 :"&#   , ' '(5,->!2?34Us635wl3]{nu}yyWhLF`HSXC\aOJT$PWdPo7Gg176.YQIk^mKzx}GF}YqtPeYsH}fW=si_dzՏ|rMŢb1,]8NH+R:='705(9B9AW!+2 '3)E0@Vr@)@8.>+;H(3J!-74I(Me.;M'NQ KJ/88>&-#(5.-<#))07E%?S+[eAeWhNWqR2QW/Yj/GQ#.=<-7E)4?*:;+8@$<<'=?"97!I6#Y@"I;/K/3F=0N98IB8t<-QN.JB39B).2*<3$B=!F:(@:#CC'S3%;=%D7*96!F6(?:$BA'D?%F:&A?(D@%B>(?B,8?*8:&@A(AI(?B.AC-8C*;8'D:$<<&8>)68':(.=#.;&7;%1>'97#73 77 5:!6>$0>*/9 .81548.8!146:#,8 54/8!:4"45"65!0-5.!6-:1,, 6+!0+$9-$>-)>.%=/"73%90%>."?5 :1#=0 <1(72!4)!&(!/0"64%;48?!=:#<9$@<&3>"74B6!CK('D8":F%;B)>G"?C+BB+HI*@I/ML)FO3OP1J['BR!WG$PG)AX/A=,8F#I5(@<&PBLG#=C(@M+0F4B<8;E--:&!1!%%*%!"      %,-%  +  + +  ,%8=&7@%9E)5)#!!+4),"NH>vLVNBp9BV6ShC"Q?"/E7@1ML\f(GY5^8E(IN,\X@DU1(;,4)&&#  +   ((&4=/hJjxGI\(@O7RMUewncjIVsQ_wSkmtj_viu\Hkt:oV}x8Mkqʎђ~lj>8b6,/:60a[6pZ~rX[|RdpgS@sȞiViiRѝf脷c_q$/8#*008B,5?E[vQǛf{WH]JAgj]TlxquzZ,W_?`:_f-FU 4;&4;;(.8>&@Q1~QV_Id>KE0X^AFiY[|EPk8GR!AP&(:,QZ$%;!-"';.<2>3:&9:*7B)9;*9?7FSSOovB\>6E",5,;)?!+=0F&IW/-<2A!3Q0Le9D_'ASCfk+Wc*IR&CS5B /<7@7E%Hc)Xf4>U?9S49A##->:PcBTk?AH6FX,1.<9&>>%5>&1=*97(8;):9#3:$4="/8#25#6434 79&3B)38(3:#19(66$C3!D??>"8<%<=!6F=8%,>$+6&,1"4.89$::#<@"8G'/EE/L;)C=.NC)PJ1LA6;D38?+=>(LC(EC,;:094-B8%>3'86*@=&:>&58$7B*>I)C9+I;0<=0B6'?8*??):*7A+850A<):E,A9+9>'?>&9?(C@(=E(7=&87!C="?F);9'7'-<%15#66%.5 21!68"8='68)6C(.<,19&/: 253:%4< 3B"2>'1D$3C$6?%4>"6B"-B$*:%+6%+9%7?#5?(8>%2?&/7'1-&4*'94);3*85'<>%77,22(:/#:3%88(E2&C1(D>%5D#;:$5F"1=&95&58$<8$FED(>E(97$E:OI!MJ'LH#@)K?%;@.;B2:;0:C1A@(9D05d)02YJRu*Tn5Hn0=Y"5N3L"QZ.Vd/=d'E/%!,  6616,+wVDT1>)*;37"P6[M%rg|odehyt߁sIt4{@Jtuan{jlPH`e|raSAM@fo=IeR]pYQp5QcA=\;@@VhdLM`g`<`p@GuYkhp_Unn~}|{zvYfcPxVO_36:zj0LIYWM_@S^FZ]E[=tCIDxazGw6Tg3@?KS^yxnƽD< 1#$,18 #""-NKK]+7E0;G!??>FTU-;T'$&$%226A/_o:glUA]`UPCOA7f~vrXwGSX8EY?Tates`Iitmz{k/??6[iEq5hw#Mc*<,6;1-":@1JT_rtNZh-=S1Zi7DQ@fiNn5@V+/D%/? -9($1++5A'-8(3.@!3D!AR*8I)7F$S3U["C< ';Sj9K[6X]:lb4XF//9+@I+;G.7#9 ;< <"LA'&>8'C5!BI+;J0?B+8E&@H+CD'D(F@5D:+FB/9C1?A1CH)EB/FH'@F(AH-8;%G>%OO.VF4OG5JH,ID.>C*>D/?C.@B/7D.9>(:@$C@*CC+@:09C*;:)=8*>9%S<'=I)4D*6<(AD,DB%;8,7I1:4)8?":?0DD-EC-A?+;<'DD*CG+AC,HC)GG1LE0>I)D@7@>)CH(AH'FJ-BO(!;G'1<&7;*;8)8C$7@'7B%@I(>G.9F(/<+.9'3:!6A%;<&AI'1J&29>; 3C#18#4>'4B&2?(5;#32%36 38#3A%2?%34&88(87#;3%;:*9B'8B'3?+1:'7@,:<&;?*<:#?C <8 :A::+8=+9A,:G2ET"OKJP:J4'=(/+$"+, +0%-.+$&%!3&"      +   ( 23?7&,K(7F#$ 8"W4R2.^$/4&90TlRzCC/F[0j+zo(w=a5Yv4Sb'0S =PSS#Cb98J34E%.<%".4 4)%  2 /0-A;0>bXekblwW$T a|'p~JWheus[xQR_bgcTfsІtwrUZI@S\BP_DJ.OictMxfx68uTY_8GMV=Q80J;3A-JW'7C?).6]QDU7eS=#P6% %43=AR2MRBDKT]s~},BK2C%K<KeNjpv6Lw8Xg,>J&8F&sv3:*pmIFU_.(1>++9MY]TAvDŽZws{tbzDVsNMs3s{]jN{XDo9JdFA]oۛ\7eYB:E)./>/!(*A8)B9>usbyLdt88D0AR9;OlgEOa-8@,.$BGD@,2AI$AS(R[0H\+>L$MM!CK:H"AD%GQ$GY*Q[$=J?F9K$HS0BQ28C"BJ-AR-KaJQe>euDpEexIoKv|?q|;w?Zm5OcCYtBtIq~@925.KK*NKCGAD.=M:;H+;H ?G$AS-IS%18$17;8L9@P('."$'$!)++8)7:6813(* $,(;1BZ(MN./(($-.1JNR}Nz,NI+045K?@W?\kD]b?MZ8^]6PX1;I1IBMESHSHRHRKRKWNPRPLRNIOILUGHTESGLMNRNSWNQJNFSBNELALEJ@EEGFMELyEJrBAI=A">B$AD)@J,9E.,B-59*8?!=D-9@(=;,AF):L,CE+>H+GF19G+9F);A-?K+J.CC4JA+>G)8D/FC,BL,AK,GR,BH3:H+4D&=>$@J,LG.DN2?P5>A+:E.G*DD*;O,=E-6F)=C+BI'>L4?F3;D)BE,@G+AL*GQ-9A+?I)?I(8L+@M-=M0>N,@K,>V1=T/?M'NK,\P8L\5PJ:@C/(::'>C(<@/=?,66&;;%A;$E<';G&AB*4J/:;)<='AB+AL)CI*;L*=F&=>8G 7?%9G#5B$ED#;P%BG!>".B*-=%75 35":4;B'CD,8E1EH$;O59O,AI/@F*>F,=F,BQ.DL+P_2Od5`j=YX7Ng6TK,Hb+]^?YgF`oJbg+\m1Xi9F[6J2;C$$=(+($#*!2(1- )(. )" *-(%/6A9,7#*+!@A$:I+$H5010YPeS \z8GmGFM3UV?v}.5D"AL!Pl(ir;]y!BS%IQ+3L4(?>*&6!%$6*1* @>^V"\mAfABZDNgMSEXnMUy_tN[ml|JU]gT`7vKW_cbfl|AaAOQN_U;PE5WR(4L"HM33 +*D@^^*SBH=!A77ZLQA^hJ[>'22A+AR/\_Ljqm@laOVi>JS53@-N2BI6IP4KJ4ON4OR7NO6OM6LT4LT3KN8MO/OT/?P4FP/GM.CP/AP/DO-CL3CP.JQ/GL+AM)AL):K)O*=K+?M*?G);D+9C05AJ1BK4>Q)J0?F3:I/>L.EM.;I1GE5TO/ES1AK0;H-;H*:I/GJ/?O6?N18R)AI$DL)WU1ET2IT7=T+=M,AH-;O/8I.BJ(X(@L.AS0>R.9P/>K.@O07P0;H*8N.?M0DR@=R3T.>N.@D,1G,7C+A8-B@&AG&FP,AI0@O+>O0BE0BP.7Q.DI28Q,F>%BF'AU.?M3>I/8S-=@,=I,3B.A6$>;.DA#FN*BB.CI-CR4AJ0>L+0FV,ON+]J.WX.nt4Rg2CO0J]H^U:W`;fm:ZtBQo5bcBWs4_b=gd=SnBdiEwoFXtHMa-Qb&U4bl@hk)qj>S8`[-;]5%;$++*1('?-B@+=3C"3C,<03"(3=L'3= .C&+=4.G8:D*FF*=&;8"$)J3]V2|sR_bpQtr3~t,W}/HhCCa0FX#[U"]4]qHVo3@k)3L1H\&NX4JT[[#En3IV2Q`DGD5]Y-R_+HaVY$-)::?,^V@`,7U/=C1Ug>ecu{SwP;.czTw^H{.GfEvhLLu\bgj~Zg4ZfJd4JR5VqK0D#CN/1CT,?\;QRRLF;SGZDXQ1X8"-676;[Oob>~ER#T@y7QLQ|RXzHJJy{GߥnUnpvjw_~JAoyvYcx:c\@[eF9UF8G0ouI|wΌʮߪm@7z6ti5bg8Z\*nwQa]u;iy3_QSf9JaGDbOUrlgWWZ)_U*m[7_a<[g?Yb6CJ)8F:Wa=_s;Yg.U_0c`4Xp=ZzPlGwLEYrDQ^`^7cf?jk6`c4V]Cpl6ah1ab*DQ(JX,MQ7DW6Q]3/ ,3'6E7LL7PY-;=:@7<7AY8QS&KS#;@52:4&.B+4.M?"9B0IR?agOLJn[o_M]F:VE6D4>?%;-6#0131*2.7.506/4+608+</?(O .G"+=#-8'1+-'1*0"1'.'1$7';$8)$>)+;&!:,"6+(:+)<+%=/"G8$DT,CP3EO0@L9>J1CE+O+7O57H6F16O/AG/DL/BO-SL1@a4EI1@K3=S3>I1AM2?S1?T2N,;I-.;='@G)?M1>M1?K*FQ1?U5=N0?@.?J$Z0HW3X3CR3@Y.BV.?X1>M/AQ/<\5DF/4I*=C.?M,DP1KM+=`3;M*;E'>E+FU0=T/=Q09N18H.Q0=U0GS.CW+?P0*9;/0;1:B-3H3:D/6@'3D/7D)=N+9L.W3CT/LU/C`:EM(MR3PS%BM@E"BC'7I#;=9D%=D(?A-HK'BT1DE5DF5MT4DY,CM=IR3Qd3Wd3OW4H[.OS(CG(HN8SV,CX8QPBVL;OKES^6VM;be4\Z2R];W[F_eE]j6enKQi;hwM[LbS8kyKbT^c9\_1dk>a?}yThjET{_SZ<:_D4L'OM-B4>BF#7K-;?(/D.'@''3&&6$-0>0#CE-?;, + 6? 78*4A,7I38H!GHAD#793ivJISKD?<_hii\ZkCMT/dc/`j+q5UNs@Eo6*K2A9EX(W8bl2ij0Bf7:F"I`5\T)o@e;Kc6yKi.yb&hv0[\ECh:5H$TH"z9N{}Des~We\lyzctB^YLeV8L[M]n-GF4:@usI`p6TjG@M JD*+DKOLa5Jg@9M5IV9'C3/D!'G#5+3-9@#JWLF.DWMC/;BA$MW"hc9iok>^OBE-_h?]^w-RY3D9-56&9%)HfsɚZDoMS|Wss7\I\{Zputqaq7sCO^/CF3uVŢ_pL\Z*US+QaHVCN^KjayUCcw;dq?tyAwH|=xyHo}EyHsUy_YTK|MJ8[o9N_1N\@LbFZjGWsBZeTYdK[w^rTuQyxMujEpsPh{Jjn8khFoqEqzCfr5\a0Y_7OS=Y\CT`.OH!??'=K.DZ8@G.1=*/=2VQ/ASAXw@[b2MV)5<(,&,0/]V*K\4fe=KPB>D@KJA5D2+;47<.=C%3/ .!8.$ ,#)6Wa.J0*7&(  '5 <8=:">;$8="?=%:A"=>&B;#CE%CK-BP7FK0HN3HP2EL0AW2?K2@I-FO+AR2@K2I.@K/CS8JN)AT4DM5HQ1JW-O]-DW2FR0AQ-?Q/AQ)FY,HW2;X4?M0CM-JQ21S96J1:P+DP5AQ1-:L25J3:G,@E%@"4H%/:)B:*8Q(:D.?L,AQ.AP*CT0=O,5J0BQ-IR36[8=J(@N4EU4>T5CS0=X+FS1DR)?U)KW'AS+IT*@a5IJ)>e/OW-RY7T^+Ib4QO8Ge=L`:L\:TI.\Z9Vi0Wf:MS:ZV-OI;LN.TT4KO8KK:SR9ZfGu^4V`=S[DYL>_PF^`9TeOS_;cVH`rPo>`oIYuJ\`F]wSehRc\W_j:EU58]'09!HT?2=BF)DL0CU!4A(;?%>;FC>@OD$Ob@>]6Q 90!65"FL+2C*/L17>1B[?KS2T|?RX`Jm:li1xXL>A4b7R"e%z_rmy[gjlvl\Dv`QA895YzRnN{j;KTJ<=8F8BT+)L<8G(>M#qq9\,mw:@<0E)$(>E!*%*&&Uk16a:@<0TM8R^Cpt3Zj(xFi|Tsjepn9x}1LiQOSf]aD')3@"3>"0?5"dt]\nƃiJF[~dv?NdbIWUHfd1IWIaWHOWxiJty1[OP__{||P>EI.gbO{vRersJZo5kCHbATeeEm0T`TPf[immhXw[v`d~Uvjvdld|T?<=;VwvYڸu`[}JykBYY2[X3b\DaREAqv0WY4`q>uqKJtFwOEsr@{z>Bs4zj:tlGho:guAvAby4`n6Zn?N`Qe{[y]~HNL|ZLyDnP|NoT_Wzg`QVDk{8`j:LV7J[D\nD^~B\xD`n>Uegehx`WxVoyUrwQo{M}Sz~TnsQu~Fms>msCkk@kh>\V9`e3dX/NW7RW5UV+DD%553@O,US@l|JN[>DN3DS"17 +52Q)C@.>H+EE-:G0?J2FG3FO1KS6IY/LZQ2:G8IK1HM+NT/LV/MT2;U7DL36M*P0>P./Q5=E2>N+KJ-DW6:W54L3=G3HK)JY3CZ2@W.KR/K\0G_3H+HM/K-9M478099*?9$=M-BE0E-7J(8A,;B.FD*=N.GE+L2?N1?T*=X3>O5ET2CU9@S0LY@Cb/Dc:S_.J\>JhZi;Lc3P_3Nd-Fg5XZ1]`1Yf+cP1H]=QGIPnE]Z6c[\qJSc1fo,^:dq:]7bq1[uKNV8Eq9OJ\FYG1FGAM.H`07\*,E$'1$aZY]~jWrc^zV\wOu}NTAKi9HnDj~NnAfx@\r7z@Hz;_Ir@Qa5GR1xPkp`aYb`o}nuVhxjqHl>WafqZwuFREbrOpalgWPQwI]DrfjlŠz͟~{|ftpN^Os^ZQz@vAokAgYIptPoyNvDLNTOdf[fsOehL{UUKFIGuuCuqPcnD~NqBmu?lz[\t^}Zb{[u_IQzUbeU~[yduSW|V~Gvq)  +  $ " 7>4?D CF/@J4CB6FJ1>N2BD2DL/CM3=H/>F1AH4GN.KS1FW5HR;PNBK1IH)HZ'JP)OO&BY(CS+DO,>T*?J$FG#HY0>U.FN2Y\2Gd>GV2UW5Gc5IU5HY1D^?D7HW:Jc=OW@I]=Yd?IgCOd@D[5LX>JR0QY2Be>Hc.W]?E^BTF1Xa<>S-B^9U_7MnC^h6Wu6PWB^h>\dIV[8fnF`lEY^BnhCWqEQZ:[dQmJjHh;_p.Ql6[gEfs?}Pat;vuBhS\oH\o`lN_R`_HtwC=n.S6^_F\Xr}^m?_u8n=GV:X~AtJZ}:Sa,kg=tQjKrAl}vjhDTsRd~Ioh?UxzgmlHJzmIxcslhz:Tq0AKO\eWY3^^-jj7OZ13G-7<-RV%\s+XmCGn&KK$Ugt?7=U$%!-*-*$!33 +%%*(##(=68o^A^N2DETo8KYlpQ]jKxIgfjvƁ{`:=PGJ]Yxq~CvPW`xZqu\uvymGiK`v5Op0Vp;Z~:SqKϔubaM=bsBfgw{WZvjIgmFH_A-9JH?UOK_.:>') '*')9'>:-97/310F 'tf1"@2;+(6#     2=3A:#<;,=@1F:0>L*=I5D1VO1X_/OU0KO4NK0SL.WP/PS8LZ:GE.ND*bH(TZ7BTGEL7FK3RY,]X>SbAJS;>T6R-5F(7F)9G)8N-1G/??+;S7AK+=M!=P%TL-MT3@Y-?M+BN$AP0OX&LY.I]5DP6EG4GZ7EY?PW:H]6LW6Nb3Sd1Rb8TY6Vi?Me8NW4L[9EV?XP/FW-[G=WU3FFFRM9MQ4NH:W\7RV;eKA`bwK`U[uJg|Bh{Ko~PXPiXe]N_P{(]8dFIv?ni*LV;yyCkzVEg>4['f{(gElB{:Nw=LtE}6j0Wa?[m@cw9r~GjXk}OBX\mnwtdOTeMxmIegc^ckUab[bP_xgfWyJaTWp,_2>d8RgGXvZK_Q5T>T\(HZ.8OFnJ;lH%`;88?+Q=J>,2# "#&;78CB]"1T1C4C -/ %<.:7-=D"@A@,A&*.60&')%FMMC*N\KC;c(LI/2X.22 U_:Xs8kdY|zy[CZB0:<,=o18X;e5fo3Yyˌ^8\"'#%,"&.2-lU>DEqJkZibX|߀h`jbpH]f`nbAUxEJP0WbxWTpbDbRO`iWmD7C+6<clJxPwIoZahn`qFlQg||o|fs{^YOvxPksZqZr{Yn{\nimgWvO~QwzLb{XgxUufuizhk}fbeuAO_LVt\pjS~9QM&1:/BL$7C)3=23C30A78<&, &0#!<--, <=;H>.>>+:A19<2:6/A#'`^*"=157+61-&-,!( 4IU>RO9RZ3N\:F_9NSKaALX6OM1JP.VN5S99I0KJ+=U'FO4VY+S_0VU0QL3GY0L[3OZ6A\79P.2L2:G';J%NL'T[-Od1M\?MW7:f1TP5H[5\P/Fj+BT5YO-Od1LS6Q\0Fe;QX4Pg9Pd:JY@FS/IT HX,W\9\kEWi@Qh$Nd6b\5SqMp>GW3n[2OuIKi8Ng?di;Qp:Gg/H]4J_5^rDis8JpAbf;J}?FeFg4OX3E^8Vd>Wo>\dDGjAG`9H^;J`2Ok6Ok>Oc5V=Mj7QO$OU.EY+KK/GN1FS/JPHRE+RO4SK/TN3PP3Zk@ZXDWiPi]HhlJ^mR_`F^}dcwWQlKcqSJyPKV;WkD_N_Tg}dkplYwbrx]qP}]V~BS`W:E]M;|euTuAqG`NN|Ifh;VJ]e\Ol=[w;_o1xAOxDlcQ\JwtbhupT`xE`xUNnK~tMdhPjVQmaHaTOR8C_j*@%Zj9HSn>I^:aXMxvbgndfhFt'0-" VFa\3{hUBM267/av__UkYh{AbEXq>C[-@WLfrbcOwN;N8@X0rgHHs-BS,S[FhXg>F,^^7\bn^gg}5i{:F[=gr`]ueYoIXg_nY[hѓXf[csTShp}q|vlyuaa`TX]{UOqZokvl[SVepikpqvePQ]QzJ}Ycdgk}dnZwXuVp`jj~]nou~gTIszOpKixIk~b}h`xklZtUQrsKjuN]|\wq|eVZhbhbixHadB]h^]unszCQQ(@G2IU-8@'.=.9A+9;/,5'."2<"9%!2 A.>?">J/43HD1?D15K,8I2=G)?G/=F.OD*EU1=X4HI3QP.MT4GR:OG=QS9KSDMS:jM=ZQ8WM>UY=YU7TW5RV4E^?HR=LQ5VT5UM4JZ2GS>CT7OS4J];MU@JU9LS4MX5MY9T^PT2W[6SZ5W[6Ti9C]:AR2QT-F`I`6Fb4Q^8R`0Oj?O]9XtASg?T]8Q^9Zd6Sq:XoARk=Wd?Rf=NbCHhDQ`DPi4Sc8OsBP\CPdBNk8dd3ut8UDWfD^l3Vh?^g\dAQn7`c9Xv6Yt_u9Qu9E^8U`>UmWSjh[}l|U{MkHFsUaK[W@gMKb;ER4_jG`m_oxi[IA]B^YQ]wzCk0J9.;BI3)>"EQzSP2}J};L\ ):"3!'$*%)"),)W#  +   +ECO;[R+(.5EM2DVNk66=#$)&, F.Dd7< KHJRFE?;[i/q9Vo-h}9EH3i6He2ewLV=kvLV`pcxqg-CQ,9I:iwC@T3hx+fy.^aX{W@C4SI+x:QRTNeXa`Q_:T_Y@LsOYeQ{Ev3LYES`GiyP^ui:ZrsyTk6kjOuPn̎cjqwxllrlhkn]SyGj}J}ܔ׆ɦdcngqno^epT|fj]m`[^tuthu_{TguT^qSp~Ss]{c{Zw]}hgbMuKuNQdpZza{l_aTPswEpUogfemVMaxWYsRepM`oS_s\Xp\UdF:G2V_5Rf0LQ!4;(>G.IG(?>-1%8@56&  / <,;D#@E06E09E/;E/>@)D-"D K"@$%7$'B8)HH0>H4>G49I&>?CBLH@J%>M,ED&GI+AG*@E,@G*ND)AO.>F*HE0FP.GS\H6QT5UU:VU;ZX=Y[9_\>KgIJS>OK1IT3PR7PZ3=T6AK2MK0KJ+DS3GT0JT5AU6AS6LV2OU8TX9W]5H[5QX=H^6H_7@\:=P7IR-AW6FS6F]2NS.HX)LX5VS4Y\3VZ5Wa/QpZk6Wu?LeIfDX^7Vi5tg;jBQYekKU8`qt@PUDIsAYX=am8IkDL];cf8tzE^/i`Biv7Q~IYj;KaLWT>Ux/Hj=eg9O6LVzd@QpAQhEZiENuB]e>cxJP~?^kKYx8V]6ug0O;VS5PX)V`.Rb?Se3OU.Nc6Jb9Zi9M]?Rb;Tv?Qf6Z_-[a=Rm@_|EXp9[a1^|B]jBY{C\o>[l8Zr>SqCdrAfyITr:ay7an;[|@PrAQc5Zp3Wo6Kf6Yn6V{>jrA\xG_nOd_cyPeAlt>eXOjS}a[wKpWRxV]~^^|P_ElUQ7vDdavSH{TPNNeRvjg^wyqa}sgiGlRpx]~VSwYF)Jf7o~FE>WL>VnfLM0MU;(B]=UKQ\0RcCbq'ULX&!"1=.7f6]Z;:V;G;LtgRRHeoaw|uqogibmGaRCV9;APgXzXVjLvL{p~bjRSq΀\q7H.AP+CPGI!LMFOW>gNtPU1AS,Q\/jlKkh*@IptD}Kz7bpVnE=TB\YPHF'{s?ZfB[W7>F-LQ4in|wMNk0MH+OK7O]AtRlhmFho7HZ4HZ?ENLcZo`b{VctrƥcF]rTadŢ}pd|ӀdžxvykZaewϩׂnY{\zr_gTcnpivfbrgTr~UW|HjxN}Se~[zg|amfVyd`VsbrxlojU}g_qfaOsWiSe{UUn]SpQ]rV]{VM]OYoPc|R~;R\Crx@XbGZf7TT/HG4GP/HR(RZ"8>>3& < C4 +<>!BC-AI.@J.7I+=E-=4&=":9"7C4FH(GI,CK0AJ1LJ)NH*IH,QK0P]3VQ2LM-PN3SH0VU0XW-H]0LQ8HP2RV/V[4`W<^\8_W:QZ=SX@TT4LWYtAZn5Q}QZkOdw0n@_RXDfo=_5go3LvBEY.Rc3^f=Ms<_iCO}AYf5Sp@PjHKuESdcj9apC]q0co>Z?kn4lxL]{;Zi:WvCOj>ToC[NWnS\8ak8fz4qw8U@J^1Zc@Rp9Kob~4OtGKl;Z{Tak8NyHQk@IfJei@O~LgzCKdgzB]{J^=pHQLXh8jCPXdwGhXiyXg^]aǠpX]n~nsc_|~rwvrmctuki_}}{oǏpxZmemf_sDVF:u"u{&MY*/CL`j&XFk;f{!>K15Mf@W-Yu80W'DF/,B)27 6XBC+Vj.5J'," #) !. %>*2(108 -"%H:,73,A1)$GP:FP[QB eUK"*3)JS4T^'alLxL,|,Mmt}Z^`KNkRIJQvNPbSrr[};Vb'gfh`qGEAj|AXypd[Wb^vSm܀e瀮~oIw9Rg5V]?jy?eg=Ys8Gb[5JB/@/(?PVkHnLzga,g]^qY{|{fr7TxI2F2LOHQ^;G_:BX,PUAKRjU?LQ[S/@fpP]|JjvabpNiW=E5JU3=I,?C,W[Dt>jtPk~l]iPp^t_rd^ITn6CTMJ\jwrzvˏӪٱŢ}jptep{ml~hk|vrpXrzlgz]xvɍwsnnwd~\sJ\tFYoKn}Mq}TY}Wre~Vu`XzX~quwdfzo~~`z`y^xetks]}[vVyF\bPanIR_OaqTv{QXmJgrM[{`lNLX\zHT^@KUDMZ7AF3Q`7I]Pv>r{?,&  +B E9:E%@A.AE.AG.DI/EE-?:'91#0BF3CL+GJ(EM-II0NO/OL0MR1WQ3Z[6WPSX:IPY-EW2HO2JY+I\3L]3KZ.V[-Tc0Oc5Q_:Rg;I]=Fc3B_8CY5LP-R^)G^4E[2CR,LU,G],DY3JP&EX.@X4PS8Q`@OcDIc?C]5O_2B`,FT.HZ3S]*kg-[~5@j;E^4LT2Od*A_:HZ0P\7?]5P](Qj8Gb>K]1R[6Nk2Ca6H^4N\+Jf4Fb=FZ(E[=T_2\l.No:Og-M\4UW6Ke0K]4Fb7;a3O\4MiFZk>On=gf/[}AjfDc@d=[IVqU_qLrGTDanHH}IKc:Wq5Kh>PsBMr>WtAb{>]rBr6Hw?e]7a6^HZnCYy1gmAg~5U|PbqCLxH]p6oOiDlF`FLoKjk=`F\FnQgNlGjMaBZ:w5XKp};WwL`iJ\c7Wg:pm1rr@f>ax8ju7jEUmH_JUyQIgyHfHKm8az=d=Ow:]p4aw?gy8W}5^oA]?XoIyLt:cXRvDY}LLnAfyPcy7`}Ah}Vx@r$"3%1QA%Xi=iy*7C.?"A@,_W<}bm?3<mL/GY>UL:\mrklro[mzdah^vilHYuIaAtbhEpD`UY`p`xLVrUdhODYUwv?aQf|UAa9`poy{nsoZ`Au?WoLcQrNBg4Rk(;B.C 075BLKns^Syir\ZyFLi9R_8@WvluwTER0id'rw9:w.Z`.vs8msnVcvOKsW)EI$3AG[Bqb`pb\b}F[-Y[0H1=I-APIZj[m,UaCi|kV_܀Ć~’ȼܓÑ薾v߄ib_hbtcwgehgja\uorJoo|wvt\|Eh;iwBszKQw]|X[Nw\l]oduxdPtVsuisZzniq|eo_wYlfwNx>l|A]oTiaqXwF[sSHX@JNM]bFlj:]bDbm:QO>eLezjlN * + < >4C>'5@09:)G>(CA/DE,?:04!#$&4>'AG)HE,DJ)HC)MC+HJ1WF4TJ3TH7^IT(NO0Md7NY/K]1N_7H_.L[/Fh7M]9Ke,Dc8M]/Uc1Nc4Qe/XlArBUR3hg7bz6Wn7Sk6TY2Pf,\i7Tr@Pl7Hg0RR/]f6T{4Jj;CS.R`*Jl8^c7Sw@js6b}DX{JIsHIjGZpN^wWXrGLtFXgHXnA_t@[p?^q6Oj>Go3M`vr3GUAW|CILfjDaE[zINzAPr=`tG\>ZVky8rD\=d{:nNmUaw?{~B}A}?Z7Nn@]l;]r/f9P?Tn6ad6\vNe{Iv{MeQsxOqOZL^Um|HVGip;eF]B]j3hrHwIqQqVSPj{TiPYCYp>m{W8Pj6Kc/GLPddLFI|\yiKsTkYQgxHV_v30AADewJpau:bx>y=Zڀ<R3@J.9I(CI-AG+=P,;P29J86J2AJ+]P/kV1\T4NT=LS=FU6GR7N[1Y[4NZ:AZ3>U0LJ.S^,T`9Ve4Hd4BT1:U);N/CP)FX+K\/Za6G]5ZZ5Fc6OcGDc4QV8N].Ba0BR4NT+B]&C[,BV)GV1OX2Mb3Kc1Mc6I]3Ha9E\9B\->_6<[0L\,\b7Qsfv>_J~~K{?\>mu6sQVOYs:QK^+k}JZAjz>Y;]2Rq9hwFM>UtAfGbJYyDgwKUDdlDshedi[jHn:iIZF_~HWy4Qy7Z}I_v9Z{BirUiIs^sMkCUs=Pr9cu8_CfJfDjzEuHoGtIoAnPpSnG@IDrY8wHJ@sIm@`7wAMtBuBhKv@{^HiRqzEURTLkHQLcMDuEUp\jhNrYqwex}sXrq}ϏƑᰔ}~wȑ윤luyWotUx}~т፱Ϙ͂cQsS[y^킴rWGGTN|fFlK{vju{S}-Rb4dcOThMBEqSctLq^~kGUwVwZfWgWF6V^<@QGj\enoF_Ygptdqc  + / &&&#%"$()$#9$+!!/<1D6!CA(80?;#E8.^=(XK/LN@OIA@N6GL1HM4FH?RL4XM.HT2FM9@J(DF*?E(C<$?G&>P/=L*9L.:G-NK#eM&XT1TP??TV3KN.JV0>W2HQ8FU4IO'KV)O`5R_=Bd?HQ4EW/BQ1;R(?U+GT*QU-Oa4LY5H\3H^?IU4D^5AX6E\9EW/I^0H`1IZ1LY,B[1eT1F}4J`7UaR^-^m:lh7a=M}GLg?C[;M`-Vh0Mp@Rc;JtPl6_rKKlRagDY{?JsW@bAnIc@^|@U{LotK|ElKprfNe|B^BPo2[u7fk>R<\lD[DiwD]>f{KWo8Uo8PnA^oDouPMGLiHVi4RlM_zFZQOwPSmA^pKmgP_;WsCWl3bl?fKs?qMbE^>P;RtDo9e>n}I\}IqA\g;fIi\fzBYOaJT{Lc{Fb>`@f{;X4by?r;x7|PpPpLVMOwAZt=h8eKtKl?d=zBvJwK\}DsBm^~K^CG|QgYHS}IDrSvM}]pTzVSpJ~gXPkSg?sf_JfRqHhSoGr^lOnJKsV[sKj[rV]fRTiijrX]qV~[zPkTY[UMSwphJ{f}Yl^{wѝe|ky{edWT`[STpaNj\PiәuÇԮ̑jwЦyjZ٣_fsYr{noNXlqbOxbpbuTLtW_um:mnA]v=h}1jxeWgaL~jyGymuFb\WqP`nB^uS`^muDWx3KZ"KO/q8fT`flXThFmJLq1W~_Z`bv^p|Ok]bPSt;aoFhy=QbA]jLcoZQk>py-ki9YtG_qF?bxGtTb^rzryNac0KŠʳ尛ɠǢƬzh|apniWiosUgWW}AYsLBa7NfdMT8IjEOhVeYmWpREa<[tJeHTp:XnFbzTdBa|LqLZ|Qj~DOcH_ySzPpP`uNtUlAjwKPhIWb7;=5j~3Yk=cK^vWHXElqGcq\_vZTkS   + #6"-"'/&:-75"0:'16%55!83#G<&[C(WI8MA8>H2K6BA*LB'JG(O<(AL&>G-R.OO+C])ET+UW(H]&F[:OZ2M],I_9G]2H_6IY0N[0FY,QY2@]1BX6MV0G\,v_0\:WsDTm;Zm5Qt:Vj@Jx3W_/Ge)HT-?_(O^4Lc9Tb,bj@au>OzJSePVl0Jm4Jb.Ng;Pk@Io?Jc:Y_5Qr:Kf?gg?c{@ko>jMSLPk/l\*d/l{>g;lJiBez8XxHTu>Yc=jr;n?\;hpBj|9ZJhsFc@WN_vGg~NeURQVqBTvDduEu~Oy[eZf~TVLV{@LoDsj8_y>cAcsA^p;bs@ZrDanBczB\h2kn;Ty5ew;`L^l1Vx7\k=Zn7Zu8To;^l8f9eo:~H}GlHV:Up?kw?[s^[GXvAbv5Hs/UkEdqFXGCkZP}f^uB[?Z{KSF]C]}Bh>g?_XrCnJ8mES\eLoxEfCY9U|9hADpKlKcBd}>tfbVc^r\XNjvDfgga]ijpv]wDzXzQ}Wx`tSV|NuOZtlYd{~gSyM[lZ|EuPoAiMyOmNnNwCvYeVeZjWx>Y@}LyJy]~_w}ffSpNuODxSqgy{\o__օtyYnJ^qt[hcgwrjvDpVVrwotUR|Z[b{ez|foؕavsl|ӕhˁ_{vcǃqlj֣͎̉p\Ygpڴiid^yWvcc~XFabVAp^dlQaiSYPd-YqMXa=w>|}B]txSo`x|[FnCduD~LQ}Nci`ILS5p>hyDzKk\`R`nOeGxJAW[};Vp?r?XnoISe`\_[RmjcfcPcylnaj~C[qQbYgXmZtVayM_``ci_bnjStIcens]wRE\b@Y_OZlKSc>Q?ZxIeGpAf|?Q^EUYBLYE\cEPcN        4#+!&#.0)03+4#+3!3-.991&S2Z<+S=4D@5A>5D6&FB'HA*IS(EL=LF.M@#EG'GF,OH(GI+>H1OF,IJ*II)BK,GK,LQ/CY7GN-KO1IQ.FZ3CR09K,`h?eCMHVb.RnAYj;YsxFk~@[Jmqza}QPO~O^IXlDrMUDxRlGLVy_UWzJQ}YSaWJRbncPAXqIU\zS^lTZsL`ekuvc}f~ÉqDvmΎwrу֪ɋlkkfMOǙ|SnMESe\rkxzzkā{xesSĹቻxzywlƈWyukZ{b̀YEdn?CyXAy=zdK{pg^ygtghzFpnVa{dqX|y^cmdn`nbnmYE|dloTdMyfhWO|B=sOi_}lm|Pu=OfAVg?q:AbEw`zRtRpPb~Hb?SsFpEuGtJ{seqrjokqtwgehPtlqxtj|t׹gvcsku~P|RI\>GW68FCn{PjzUr}RdSiF`vEWhG[k>R`>R]J      +!* !% #"*%(&+#/*&+/2-@3L7'K9/BC2CB7B<,B?$=B'AH)OF5?O-AB,8F'BB,@N)?@2D=-P>,LG0HN3FG.>M.AJ6>V6EI1GO.FV4CV2OS,PZ3QP*GY)KX/G`.:Z0RO1Z\)Il;GY3HU.MX-MW1S_2Sh5Im6WX9Mj*Va-[o1dk<[o;[l7Lj5Kc:H[4k^*\{1Vr4af0er8MCWfANm6[h8Ys^|=_C[MRsVGq?`h?SDLj=Wm>Fq1fi7XyA[n=T}?gn>RActCT;Jw:Ua2_~BlNd^d|T]K\yBgxJ]>Rw2go:cwHUuDVk=^yAWu1NkE?l4Q`4^j/rVZ|NSyDXq3RrMj|?_En}Jm}:OSQaIbr2XxF_{G`nJbw?Yp>bn?Qw;]qRPs7Wf;l=fzGY}7Sl2Ql8_o1U}@PpFrm1\=Wy=co4Z:^vBW~Cl{NnieZXUV~M`zD\~inl=R6Sr8_p1WHcs/jzAjAS.Pq5Ws>_DWoEoq<`t@sPNPjq8n~IZBolIZCiqFmZ@v~zDgDAX@nNauXCnei}ODhpSIdEqW`Tb;CT@T_6Wz=bx^c\cckh]NLeGbfy_]zXkqpzXVjfyɯs|Lg~Rm}Qx²tURgLbmR|Z`}kn{R\nWsPCdt3Sg5jmBVqXVmKZgF=QIBQLXh7NV0O\8hl-=N5L_1Z^/@UNtEQfºئqcϺRQofr}lzlxrs~|ab~Yi|RrcuwypKd{PUsTTnCa{@[sHeKVnIJ\?Mb:Jf5MjE]~Li~{nq[vTnUgvVYjNMU@HS1IS/GX2BY07FO`axRd{M[sK[rH\uJbsDWtJarA\aJ         %*&#""#"$&&'((+,---3:2C;$:D-B>3L?-?D!:B*A?%O?*8[,><1:?$G1IH+DZ3CM.MH)LX3IU2OT-QW.SY.M]1Ma6F^6E]0OQ1I])N`@G^:RY.Q_/N]-Kd4\`1Zm^nBaq}4kD\Iapw2kNvHQIhy7nDpD[y?lw9Q@nu>i;x}Uc>B{=e4aw9`?i:sVb?b?\tH\C`EWy;erL^uBcCTx7j8dRd{3qEc:u;py9\4cx9wDs=g4x4f=vz2Y~.wznFXY[rImQ]EfyCj7EnBsfP]hD>|Wr4q\l@q8n^mFqEugn]gunty\pUr`^d\r3nlZuYpzN\ˁViZy\`OzQWpRZgpyS}J|V|PpN[wDz[{\o{ahPgdFyCPVfVth^~GbnFVXIosdewTpy[WcoUy[Nk{bs|dpw|ď؅{X{卤͛od]YST]zst[hqvx؉ɳƟ𳉷ohɀZVqބwd|phiygq]|R^TzWIspXm]vHwDd4?`6Wl=qN]tLq[}^iMpVmszuf{|ouddXUh_q\|`XtQdcxkDjY^}UsBaK}8cm:Ul5o}4VqEOod{{sxQlU`VgSoIdtC^l0L]6G\6Rf,AP3GKSf{amTWaOakHYqK\sPk?UuRcOp>      "()*"" #+"''($%'((/0.54.64?9)F<-PA)AJ,:D;5@(9?#7F'2>/3@%m?gViAa^jf]U^~LnKvKvMXh_|Ro|?a\fDz;pF|SUqSFqXije]wJKtDjXh~J]^bAt~Fn[IQ\mCS|Hbk8mOpJ\OpIZ_xQZbDcH^Lppl~?WBa}A{5oEAA|AoQ8YgDW}>awVG}^Qv@vP]\zItHgS~OvGl=pV`L_yJv@vUwGdHga{\voiHvRe;dGzXdVcQ_x:C_D`AKPFHY@fEn@p0}KwANHgGwhkS}?UY~Qs?dPm`|v]\PJvSt`Zt~AEN3CU3FW>T^*;D.CPIGdaX~TVnEYsF_nN\mSXpFUwSsTg:  + + +$(%& #&((("'($$'())1*1!,-!<. D:#J<%;H)7E48<%>=&EB"=G'6B%F@+EF&IF2MN3RJ.NS-YR6JY9FV1MX/[U8ypA\dBm`Fd{6GwFIW@UV3R[3Wg:]dc|HP|E[|Z[zTLwd_jFcmNYv>Yo=MlB[g8_m>ZwAUp=Uh@Qv9^sHV~SRtTIn@Yk:StIIn5Th;PqGdzIQDIrH]lHb{G`@SS^tIY~=?uj?q;|@L|O|bvNhFl@J\jA[FiCXKTNrc`JoPr^ƌb\Y{FFx@BU`{XfIbJSyVuMnTqHU|PU}]yN`zcV~O}ROdQ|]yXtTvy^PviUkGgSjPvKo}P|ZoRmjTv[wZjOc~ZgMwfXu{meDnyomXqqsl|NgW]n?o?HoKL}⑁foO_nΐjc}W]}e~ݞok\^wWpkPsOnhknDZy\RuIYwC\xBhFrB{MHHpJuTGqRhgZ}lvq_`_{irUd^t_][_^YwAQl-H^'G[Dcu@lDdB|NfUrYb|IqSOhl}ab{fyHbyCl:[p8[tod\S\q;py:`qFPccwmk\_o~wɡosigpNcJrmdIy?|G{Js}EtPO~}Oqo<`mCuBYg;D^6CW.AP!4D";J)L];H^FN\1EL8PpJr;azCKpn|Wcd]vDVrXbUQb4?O)>V7X`AVe-[\*FT5K^AMp^kMwH\yMSkPdo@cgQTjRYx>( ( # 02!1'2)!# #)%% !'&%%%#!#'%.-*4&/4+93!87!38(4=-<<(=<(<=$AK,@I3FB$PF+JL1RO7RJ0PQ-]O4I[8IU:LN1OP3p[8[d:hfNZw=TnQYd@cd1Xb.Xd6Te>Gg:KY9M^;_^2Yi;We@Lb;Pd;Sc4ec4\y:bd=^q`{H[r=bq8RxBnqAfA\ERr?Xp@Ww@HwCab9VvkNMs^XUxqeZ_IvAtSdG[xAsSXd}Iea~dOsJBkHsShFxGpE^@nC{\vKcHf?Sw;}:|JwRokSpL{d_Jr9rY~z|^sQlb]dchugpTjtQRTRXNzUcA[OsOoBR[hO>qMynPPzDSvRul?KuSQ{SjAL{C{X|^oIpNTmL`B|LYV}tKMtK`~B`LngP|NlDAvVpOlPt@^SmNlEr]hG{T|ka\bEtZvy``oh}ݦioj\Gv5QcpΙolypR`qz|gㅕ`L`Zzvv㑟сdЂ~ڇ]lYnUA}9z@HbpmsXWcxJ}NyY`e`meiFm?dK~dovZq9lW_Su7Ld0K`)Na?UnFuLwWuasFgVc~Yp[[gcsyM\{LnPfMk7aNvlxYu|zrFpOhwnvX}JGt[~m{`V|Yn}fp^yo}xqrSuyu\xNmY{fn~n`ZPzsUvyNjyK]t?]rMrqOp~;Xi4Gb/DW+1I3X}NNryDBZGPZ>PwofPpgYjX_xF_rEe{5IY'=\"C^7[n@XoBj8Zm>l{D_xYqezPayAPwHYpEJT>OYHXsJ40 -$&"#!%*9&5+1/0$"#!$()(#%&$'$&)&)#,"-4&36(:$20":778 .;$.9&4;'0@!2:#AA*:F+>?)SF,ZS.ONQc7Lj7Vc.T`6Zi7]lDZsCZj=fzEgvDYzKRtE`j5cS}E_xHau2[v=js@d>S~LdnK`:[~=]x1bz<_xIEvPSa8_n?Xr?_u5lEfNWUlvOLlWkMkFhFyYRcĊs_KT~kt?XSh{CtbFR`jXPgxNjGiEySgFtC_`|~2kH^KzMIO{dk^L~x`ifmk_`jV}rswHWl^{qmAl\yGFjJ{MsRTAlv::sAayVNGmsIGpNxOEv>p[vZ}VOfbOYtiDCYubmNScMMb|NyO]sBebmŚWZS\rOJ|VSpNqS_7tDxtjak?v]ʠm?nSeipec|=gDbDZ}WkPNHiPbIzMjE\=]FiQg?tOd~KwCgPnXzgkighkqi|htyn}Uph{Oo|iƄ~jnnnk`Ԅwy\tf~oxj~sb{|pijxtjjgiؑv_}pxsSxZuOuQLzJmTwNVrTfWbJ^~W[[bda.Np/F^/Ww@RlN~\`ag[_GyUeN`wMlg~q|znSgJ~SpNjC^EeXmR[|TkVlbsq]ZRzPoL^LjNnHjWkMW7`yOjRvOla{Lq}i~\[xX`snoldidf|OrYOM~QszVtI\p?_}]<`i5Hf\UqYqGjIbw>j{M\NbtRhV^NqKnHoxCFKAO^LOdX,!4#3#/$1'/+3/2-++")"##!$!'"+/$).%,&&6$368:;=!19)M2"MM7H08=3=8$8?8=&78&6<%:;%8<%AB'DF)AH,QG+TQ/ZYA]X=bV7h]1d_1Tr?\\AJg4[^;d`3ag=Uh;ce=hp7_yEjsE_{JfjER~iMJ`Relc];df}Twcx?HmX{IHrNUXjspzLyJ[xToQtmoz{{d^_T\}MgY:AY?B\FQkIWOfJhrFhr>amH+$3&3+:*F/!8;"?7)35!//!.3/+*)2$<%08*;#D-S@6N!28271 D5BECJ1H=-TF+MH'HR+FK5GE7JE)MF)FI-IB(>D&7B&7<(?;&DA"RH%SI'LY.NT5XY4_Z4bg=Xk@ThHYcHRs:UbE`g8bkv{FsPfGWN[j4vl*mC_vFq{DgtIyZqHbNnv@i}EyxAqp=^wE\}`XoBRmZGc>Y`:cgGytH}DfUftAWk?@p:HQ/Kc+Ol3vo?[~;rqOY?eu>YW^vMn~6fTwSzkmpg`swG@uC{IxTW^`l=]{La~T^TbzSo5uTgcQalV}ǗF\dxJcIYKnbflk>wO}Lr[iNl:yAG[Vf`RWbv]YtPPxNnTzNwQoWtGTX>nLkJtNEP쀪tjzyIVtFy^Ndov]>mLs|IoU}RC^lIkHnFaEy_{f^UlYeFo@TkXticSuK^xUYWd:a9sG]rb~\bLywp[|YpSm`_rHoKlRyIbzlxQWMa~f|_suo~wvqvsV}]osdnxswVbyo\xViR}G{dsWtMuɓbyNzfUP\zYWjBzTkFkMkHeRm[dKUqHg;nJGgAk:e\EaTiZmYqOpfLnKcNuJjDpGkL~bVUaEdPuOSlO^;aOe[eJcMZLf=WFPiAJg/Ii,B_,=`sp=nC^~PatAXmBgtORNklQb{CxHDŽF`DRhRQkAGd=Nq6gqa{ًt\yEnAiKsOnxNq5\6~o_OLfBr_sig]DfBneGyQkdaBkZ~RfDL[waiGp\\O^>_NeLuOky6?{fzZsEcZL^NsmmY`ZQ7|NoITiOOvEfTGl{tMy`èx^gZbwNg]IwJ{Kr5OrKsVo\iZvXw_EZ]zEj@~HvYgHm^_\@BhNtPhHQ~XoKrFstW>f|6ZwNosEV:Ur@Tf8Kl:]c;Uq/Rh@eo:juC[}CjvWZ{P^z@NtCl\Ck{F]Q_rWcvEU{@^uOfyGaCXxH\sJjyNeMYHXrDiuI~DhImqDyq@qFhpFr}>rpKhwEzUdWQl:Wd5Fe7BU"KS)Rh4Th0SgF`r7UlB]j0Xo6qg2VCNrCSh@]|A}DlJ`QK{RPh4\m;_Fd}=bQDpGdEZz^rPd>aEy=xG~^y_wLlZ7ڢSwVnMqao4CgRm\mIi~4zeCkJehxhIfGs]ifYW~J9ERiQuuzPWhOJlCHgL|IZi\oFFlOjdht^^KjMoMjG>rE{NoMzLsJzPyez]gCjHp{;:a9`ApKm_[HnEpIj`Z6_j@_dKcbC]f7ag8vi=b}EXi?mY:^s;^fFkg;qd:eiCepE\aAdc6j^1q[/h`5hnCXM\jUXuKck?gpHQyKR^DUi9Mn;f[?as2otPv|@]JWrO^c9Wm9Zj9[n6XrATl7^w9_lEqiCkDbQNfGc_6Tm4avHUqC]nAll=csln9gPYSeBh1h8a:Z{=f;BbNoIeO_TtPn@iJtLfGpWrE_Oax@U6v>}HtMsDpLuwDFUUk`{ZoXgMqd[eaDoWo\sjgLpGEst|`vվWwkdJf?[x?^pQ]g9^RDhme}Edmo[kUs{\i|WdYsoD_C^qChb_g2jTyMe}GfPvbtrXFUuid`a~g=xRrnsh[zCWcfpYHONmFyRIRhCMe|Tsg|u[`Hw5v4gz=quEpMsZ?[3}AuHTLbjn;>A]Ej-hIVAlGh|3`KB`dUrH}mshEf_@|IpTj=r?_CdYs9jf}WihW^wx:VHevTͬYLTr_x{oUoCk>aGuQvWrEl_sHLbPuKroFqLyfjFe;wEbO`:r[vNZbHrQ[JhbaCoEhOoAaz?_QhIx>b@vE_7Du>pDfPu6Pv9[wEW>^}:]>X{K[KcD]EgPqOcB_AaNcHdJmC`HiCZ{:X}8_z?a}=Vz;mB`5\z>dCXv3Nh3Kj3Jp6Nt-Fe6T};gBlw1eu:]l9kNeJ_}Ph|BlOctAgW|wYG|B||Axu>es8a{PΕuoHh[iWrUofe\vazìᑌuoZdKQEjXpVzHPoM`}C_2T>KOgl-G`:KfHdbnugJ]gQcML]F`RTsGWn@cp>cq@`s>izIpB^{QXnP`i7bc6Zx=[l:]u@=sCJb;S[0Tk1Zr]wMUlARpCmk9l8^{NmuDWvLQo>Ml1U`4ahj9c:y[m<|QtBqx?zKw>a6sCmMoQgNNxAfJrWiVoQpIiMWt?b|8@D^AcKVsKZ~XkZk\mJjIoEaFsAd|@fQUtPMpNO{VQzRKHWOjEsC|Ez@|;tDvGixTIKWWDsBOpIxRJlxB?p;dw9zzDPk|CsLdDdC\}5^{F[=^7Xy=[;s`A\s>ZHaLbM]y:QtAq<_vBWw>ZwAdJ^SX1Q`;m]:_|Cc~RqpPW{NWo@Ym=OwJGbGYbcr_V{A^nOZxCRq:QmOjc9s=YMho;a{;jar|S^HXwKQqxJ^KMdžքkUr]x|~lS^[nXY]=FvERb|7]vKfOKf\y@qVuEmexQ]fEeFQW_~2cLyOtLexjrgqby``iFU?XEP]Usm-fdMVcbu\Gt=w:rGmHjQzT~FX}sQs@[GJpPiYeKOmPwXXQbZ8~7:eGtJe?Ry>GnBaU׻KC}DsGqP~px?xMlSi=h[aLrMR{SbLa>gHh@]?\~nRtAe1ZdB`yh>]@Yy2Xw7Wy4c2Z{(Qo1`2[w7p~7d=wJm~IqJmDpyG}TYMoDeBO_fTy|T`Weŀ~w_k<(<^)'D2L#U1RJOgictii]C_AX{NkUbw_zV|Wdu:Kq5Ps:EkCHc;JkHaZi]_m][MoNew@ZR,cnIX`?ed;fmFdyJ_`Gdl<[q=`p@akBk]7vg:dqFdgA`d?Ze5rb?Yo7\jEQY>UW/U[2^a8V_9\aWq6Gs]5cKtIsGoDpRxRq@wIa@V5g@_vJ~XtMa9k@]5`?wMq=mUwSwDs6}Js`;c?f5d7d5g:Z8^~3Y}:ihUVW`yPLfGRaKIiLBQ"jgBbcEbm@gtDatKhrFep`i@nd>c9nIqtRZtB\pCfjBY|8^nPWd@Qa8g_6nj9cz=WqGXlAWh>Z\@T^2Xi=Pf4Q`9SkAzdC\}8]|Rj{@lNzM~QlWVRK}QIaKXl;Gs@Vj@fh2ow9mGeElKfGp|?]QdvQtxDtHsN_J[RrTjRSgrb[>ngDk;cHTPtiAsDV[bs_kH[yVdwLv{DvH`Rt{D~Ic\yy[hFrLkIhZXU_n;i{D^@`~Am?fGbSZH]w>RyRfr=qJ\`RJWiEMw1Ao?g]4|6v5TcW@HwShYSNcyCw?jcnHhJmR[FYCpO{PhU}pNqXY[k{Mp>xDZTO|ZKpA`o1hCkG=Q]qm{rh|ͥkZ[gbێPK>tZq^\rlpqygi]Vqm;riexK|w{\Uqsv}\n[i@x]{a]KbPh\tjZjHm8Ax{mbsXKx`V\XMqRqits`drRzMhacPYpojSnbMIgWxnOkCp>mD88FzPUWs~cW|UjxQuDt`kOYtzQ}rhlOvPnac=n{B|Fn@qPkFTAzz:pGkHw}=|;{HH|Qt`ňd@]Ek>xJrJ_BwLYKP`[UP]}Tcc|VyC\Gn_KyFa?m?vXUZ_F]{PtKmHhGgpFgLcKOtPzFxRzIwHcIrGw?v8Uy;k~;f?^9e?yNiDnGpAWdRsW|n3qDa{=`CcG`HeWdK[?tLW7k>O{V~LZjO\hXhOgCiIUjIbLoKZO_@ZAu\SiTqV`HjBhZih^^zcuXNyUx[vi[Hk^qcRY}Mc[bwYrWo\ntyt`t]d}acVDjL]aHYO^_VN|ZVzBlMS_bvm\szr~k[ǡlڤzwswZL]0;U"Ea!Ha'=V(@[7KdDWpDW]AV_?bpJ_{VXtUg6Xv@]RbEh|Kk|Nd~Piw2rYAsiDYLUwTXmR\hJYm?]nAXnDYjAkeJyv=jPnmWYuJfvPnAoI\YMtCXfEh`9mt2dK\[`hFdr@]gAVl@OiALm1Rf>Pl9e`;V2X~TUwFZpMkuLjMlU_|YauVSyA_f?Zz;byFix9iDfOf}XT}PggVU^nOuqGgNo]SUWw`ziCSOedVgOLkj@Z6V}RXyLesFqB`]osTkGUVooPi;aXgXdMwHOUvbh\YRz{U]LkIiGrIf_xLP}[QRWpL}?\HbZcV\NXuAJx8`gE`z5`HuT_dkk[wHh{`l\SXCqv5gN`QaQ~WiZQPetIgDh\QQZlKLxbVqgxpF4jTpMcL^zAmn6AoaPkwi[_YqzSudv|jb_H\yHYypWzjmkpe_gfMUkis\Yit9XxnyEphXNo_EtHxbkbfJx:FZMjOvLosUvdnXDp^CmxfUVDFسÖ;qK{CPQpPFZ^vAxHJIaDX=bn(h}?sU_bwPmBH]MzNLSKSoZuCkEeDvNgDKbn@oTcKC9`@yz?Uo88j:xGIpZb\uzFwGeGvLIpj}_OZnarJ98YmaTgBh|@wy;?lGCtaNXX~ArZjFA[;pwOb_}}P_Ea6h;^:nOzXnDlC_?odsMGv;~L?JaL\NHZDx]MdJk?SuKszul}^z_kwpnTx;emq=hOxcoeoaƷ|tǩbknwkt\pTfCmWeTbbmLpRidZmiqoe[Q=fw=nL\vRzHuF}NgGvD{?tBc};`C~LqExFiNIu?uDvD>hAx>Hl5wEERvB^z=xN_|w^~rVMeFl|Oynwxn|ahxZRfE>U+:N7J*:R=GgOmt|u^g{iqXWpT[qMjc\bqTcpYgon~UWpOOmSVl@>P?LnI]w[dWj`|ce}Cc]CyhAcGYeQdPM_AP^=hbC\s=YtIgiJfw=eXegTe~G_yMh~JkxKgRVzQQdB\i2[e4awKTsQKiNng?oy=VEWdIOh8QjBJj=PZ8Jk2WgONd[s?luF~~C|POgjiNb:]xSjCabnwHN`r~eadRtFxJ\Y_vQ\PThlzHzRph]m\~[\~7WMUUbuJ[G`{HRs?rm6eEmLgNRiU{`zA[LhTcR`DS@fp@SDWsFgz9hRQMLnJXjA`|Gh{Hd{3g>h=oDzKlXrxGkIMIxCqjGu~obNkFcG~NuilKkHWx9TnZTuJ\Ce|WgkGkB[QczLrEnLX|KNdLLf8Od5Rl?JlCUgFel8g{8SOSoLQd:Sr@JeAP_5Hc4Q]8Sa/P_=HZ0KX5Qb;MelTp|Xdha}.}8|_;rPTqaiIhUbN^Cwz8iNL_Y\c?eJrRnPkV\7b|[[hOd@wYhOg4pBbu6pJiEk8~6zFmv8JkJoWs\]\iIFcdqrdhtSՅR^g~YZxLkB^|i{UhQoH_MǪaKpJ];uCc2f;mTdJ}KtSNO@yPoEpCcFa=lJzGzH{FmQb9dEuNC_>XtFW?tGY1V=Pz4m7f98]4X>bx5aBmFMx2[JKq.DcHZA];}OnJgGj<6YjRk=s=m?Z7Uw3iAhFied>]?lq8Z?Vw7h2nKdt9kV\|>pjrXa{_|B]E}]f>^?uBoPfpkhRSp@Nw_8U|8]?S|AiA\>S{EmHeI^j~9i7f}.DR%@K5=V3+?E%;W0QkB[J>MH<`V\uEmyC[uPYqB]o8_pRevJQiLTe=>WZiF\h@ekEqwBjL`ZmzWRI[l]hgBV|DVqY^jGiBlx9Rn>HbJAab`8Xg2Oc:Le=O`8Wg=iw<_|PosYbJeuWYxOLrFRm2byBPqP[e9Mr;WhBbi9zv:FoNiO^[t`Ty?frHR9]pG^vFgg?a=pWtK]S^{MSUZpRyx8]_TwSt}GaGT[ewWJQGiWSe;\k?TtJ_tQX{S\pCjpA[:WuDUKouR[PP}ATx.[p5g:bAZQRk;u;[^u}L_4CMmdIQH:vMtY/j8JiViabt;ULOqPhui>smLrUXTj.UnuTrr;BmEl9fP~BqWgzl`ffvG\|7R0cp^AkSV9oCemK{UIHue_XxDW1`DzIpHdMlni`rNiFhhih~eLzkl?n;fFtRgLhMkzcacPLyBVy4lCb]UJS}Cv5Ee:Nq@Ky>sMMyMX}2Md6s?s?qU]v?XbLy[FWj+V]?HZLMg8c|lZcSYlA]LU~@X|4X~CLq7R~A[~4T}6hCfA\8kJjKnXsTrKmDdD_VobO_YnEq<_v6e;MvJ}YvRa~IqK~]yIqDtQ_wtkVUm]pSs]}QwRlz__\itounrʵbw_]WpUShǂf9Vc2TX&CM!3E'Pk;bt>f)KhI^yOwDQfIUvHExTi]\qL_uK\o;Z^1=RF\oLVrDWs/Lb1RnC[OtDUcMSf?ab@]h:TuEOkAUgC\mAd{OZPUxSMmST`OZb:Ej:H]PUdAa_=Yo5EjARb9Ii7Ha4Ea1RV,Oi4Uk@^j9Xr@YjWbd>qi0]:^tOSjBk`A_CY9OZ3Sf:ek9ZtF[kR`vMXoS[jJSj@LaRqCZt6[zMk|J_@HN9`TMR@Og4BmB5Y5]R,ZxAYH_kHQ{:kiDa6f~ENA_mGy|@dLPJ\|?`FaBmNa?n6gIfe]GoE`Get0OD_uBg*M=[mUKt6P_*\qGW}HL}2Ts=`vVWAJrJPj8ooKlSiikmqJE[{bq=YrjRh…ZzpqepUlA[M~SuGPaMP^hXrTyNuPxB|;isuB7Gj]VU~OHQrmHTorYVro\}`k`yaa[neUxVuj=y>_d_bgIv_͊`dZQBX`^PLwTSh8q@ho9fP|WR}UrQb@CiPr4P{_fKr7}OhC{:b\7KJnIaBh_eD]9r>Z5dKoTbJxArKɄUf_=kFIl>s;v<@F^{YaO@hL~uSrT}Eh9>aGW};l>]AE}T~@=gKl`lzZHwVV{8n]}BdCUMgCsBq?jO]yD[}3`8Nm6jY[(St*WvVcyfbx_[|KHk>Ym6{VebC^{FV^>WgJm.Cn;Mm@a9`BiJdJoLvE_=Zz;PrJ]NeyUzE{Bd[uqa]u>_pBOtHYkIVEWlKZlDRxBUnCYlGiqM_}G`yPaoBdc:N~BLhCW]9Tk8QdCVc?Nq9CnG@d7RV-Nl/Id5Mc=^^=Kd>`\@Zl7UlDPfIOdFec8V{9DwVMW>Me2PiANiJYdKKXe;f:s@uKiIUM`vDbwEGY>e[^p9SxA[m:a|>TyBQoEPqCod7U=lv_rEfK[DsPqLdJIfQ^N\L[OVsGNDkp8q3]M}6`>XGrpmIV`]Wm6}gvlst|qhnjj\sKE{VwJwnQEmqu\vlkX{\{UtUȢAհ[oON^IO_uRlՈٌgLKTP\etbJoFפܞePrѯvn|azZRX]=aGd|5=~aBn\Yp?MJwP8D^yHZDa~5s|^WPmZF~I}=eH4rCZvW^mBr@iT}MeHzDiVhR|Adna_{|kclKsfmG`BrGeGvHI|OqnUa6gLW`XCMwde5j8gMpan_eCH}UpCd3nOf/7:nM|>z;xMq׺~jOeQuKm:d{mNUrDyQq@vYnKbzyQ~o^Ax\Fe@eP}[jr@9YxBiKtLW|;mXjGo6NBOvQ]|_`K}Lg`nUd^rWgOl^s9_k8gkQ~nc]zLcNv\{AfBtUpbb}Qy[rIPaXrO^{鋩nƒlss^ϒ|Zp\y\ZhFZk9m}F^wɔmԱzX\j3IW/B[06MDHfO]qWvi]ZWJiLoT]kCft>o\C[ZNjj:Vf:dsDrMd`bTevNSMdkJlfAL~:XaHSh?PkAMpFVb8[_+Ru0Lm@L`g5SJ8H_2KU>@W>IN8l^0sy=_UVpIMo6c\<_p?vmHi@^UZpLPn@TWB`g7nwFihG\H^wC\f;gv=Z~DNlJKp8Ti0Ns8RiBKq:doAd{?N@XoS_z>aSKuDUkRUzG_s7gwH^w=m~;Ioep]wYUXAtRCaHPVHhdiS_y@o7FpWdKhu?Yv:\vFwGadMcbW7\tBq@j[jRbMV{;ns9W:f|5{8qWpC^yZ}B_XhZya_u<^LS{NOyXMk?rq4]1R:vm7|O;l[tL\mbItZPgfw;~HhqhJfE{LoqfuUt_Ub{PXvA^cwphDXqIOvgw]dfyzhiz[USsmifjmh]dR_NBKbcZ_^xJFwYt\k{RT[str}C@_~ptqrWqN{Pmn3MLyO@nBA}Xd~hzZIBm0j7xCxzhwEH{L<@~YpskCgHv\Zp_c^LPoDhix\HN{ZRnErSvMbGcCfMkG`FJҘq]i`qGcdQsEVћYcAUi@f`zAiLsLh}@Wg:brSdyw}Ar$8E-C7aydn+C](Qf3o;lAg{Jy]pSQm3]u3MtAIeStb>h[~OmLpKGyMm@cMwl=oQOeTNiIZ:_HjDWKtOyJcdvoP~PpJ^[qO\wJjIs][xkUeykpoYuJt\xʖ{RjbhdenPċݯktf`oQ~Hey;]m0BT9:UMPidgjZf[eN`wH\~_}W]BO`PivZ?h}Qjs{~ok|geJ[T[pTQiMed?ctA\gCYrOioLe<[KeRaUOSWpXWfFqjAq}@TNXjFIjmqCmG_V]{JM;Re:Kf6Ri5lm7b3O~TXmJWk;OrHcaHgqCMMkpFv?VC[wDp~Bc[Qe]xI`zNlSl:M=]c7Tu9\p5f{~LuN`]caWO3Kb9ga2o?pJtH{NvTuLHthMSfQ>ȈlRXfr3WLL{AWl6Zr5Yv6XqGe@fCLShaVa@rmT[tPaC<>gD^๝m.pQ=vZrg?sWiYIĄnSaUHQmyLdIa:CvL]=[0Vu.rSm>bEbRH|d_XdpBil?WYjqTxCqW|SpF]\p{ZlI|DSO`whzRY?Tl:kq@a6cEwGUXjP}b_u;y=ukVVmgU~{>V?znpiym`o`|Tm|0XAep,{7}QrNXBTHTzylWYtPNS,qADM>Yj5s|0]LRoIur2m@q:h>Rz:f}E<Ww\^\gGp@[Bn}@i>D]p}zIEhVlFBplbFrT~_uYxZLgUO\Utb}WJKicjNxPcPTkMd-kMD$zP{Hfn`m~D/VӋJKt?Wm]YfZO``rU_Atd}9j|PlHYQaQdDWu]ڈ޶`ppx>e;uDm;pYWdlwAyUhMJXR7\]rP_mf=gOSXdAy9WnπiP^~dlBbikB}Px\MLsGBb7wGVVaFVzFqBeyVpYs?pGnIfNaO`s@Y}KYxyIvwj[}eIteWޚnk{OdaGQhtKbR|EkmuH|JAyRs_V@Yr*HHHl:PNz`l=a{KVJmF}OQ{Xgl_]D`?o=P~,}V}kn`V9WQxDyQQaYQfZXlM`nkkEsK~YkIqBzErPyQMdnYtR|JvXjocVrjnDj]Qygp`d=a=_;gFgF\ff`qZpWf@kDjDdEmifzKbDYtAsOp4kWd2X-O}9tNn]^gcsRNggmI|?mNzbZu5EYI_][MY|?bNv_MzXc@U}D]V^zJY?eAjRWwg^rQ[}IbWyYnRc]eScJmZ|UjPWp[{πxOSuEhIiJxYjNaf]b=bOrfojwRfc^ZSN~O{Vb_ekcrdfeu|gc~mj_hQpPoϯyoN[w>G`4Qo3Xz7WpANa6XvYavTtZŌbw]xdYuLVuMRpgstuop|jEaAC^MucXkk|alJ|Whe^a^{SgpTOt>dkJNnMQrLVcewGRsB_e=`t4bj:osEzJf]um`z?jcBo=tKeMLP[zgYbv`dXtyJUe^[iguSlyJlRqTdPgIqQXYftJm~<]9_SfxIl~M]Ge{NU=Yw=[q1Vw9RuN\kVpwBkQbWUY]sU`>fCw{BVucYZ{LfuGX{V|LL[2j|~wFpOVim|kJqUyWWoGn=Me6YsAtAk>^~Hzk{fnR]{6Vx<_xoCkVdi{Kve}dgP~UcYmjjiYA]}J}We{Ugnu{I];^zD__r~oi7^2`6xHDcy8A_6W~RpX_{ITycsXFZK:X9@d=euDZjSa~\_b{xs[VjDRf7cl9i{5`Be\lckkndJIdk`fWcb]vReoKcrHa{TVlFXhE`d5RtU`X_5VqdQuhBOtC{Gy^fZd^\wIy:hDdၴNo^\mn]]]_aZpx27W=}>Ke`~1iiM{l[mxMcqWk@vwzE=zfxd^~goJgHwVrMx{QW{Q}R{@mAAmNiApCoR|Zh{DpE}`~hYqq|zDrQwOWvju]rPSnfLbwXpRHrMo@yqk\dxZg[mFmCoLhWyBnDeFeHtMPx7`R]}4^u4iPa9tAV|=j@wPhXXmNfVvfxJsFxlˆsca5cwPnOmYw\yGkK_j^]T|H_rJzHneCiU|k~jx[\]nTIrIvl܌Ⴊdh`DsUjZdD`tRIfUUr@oD}8`v>_VeQ[;oFpO}]xL\{9Uo6cGgG]}Io_pu^h?Je9g=ZtKWkH[nLRsPu[~`tn|eQwSLeAMl=_p@o{DdMtU\daNe_oYcl;j{HmtIXyRslJwFt|EoJmgI\rEemH^m9jvJ^rGTtL]b@ef6swElG}J]VW}TnLycH@kN{]S]s`ayd[wLfS[eofsgkuYdRg}QZM_uIpxHr}LbVdUz~AmE`]TyQLp?Wj:bm0}?cGRxkd:{l;sPYYexM;lnQdoLr=qSZXz[}YqhiKrZ^ckBPdxvoZsVg@lMwz2WHYnC]m8dzNc{=vDk:_:n0YIj|iktRw{<`NTSZf?]TmPHE`kA\8VvK}%Fq\imug^`q}2NchiՏbto{D|oMI^sjfefsO~QJB~M}Ym_Ch[ImB5Mƙ\yzdqJnqYAUrW7kp9JzhkeυÃcNsg尌O~RK{lLvɪ͍dນ{b~dQwVBuwsHaEyQ}OʹSa^d9Q|_UQobQnXjLC|\i`mSn]zb|UtwhOP_GZHqCJɐTak|?ԡk;x?i;a8?qh[_MXv-|AyDVP|_}SvQxQzs:dVXkgrQ]jqv@zKyZyMiDp\oJt`eTxatJiGkUvL[z3f@fEhKM|E]=_FsJ~RrPLnOwQkQuagf`QcGtDNxTsXsMh4TMaOtGJp4\mqAnDe~AQqTk`o}hQ]~=^V[_vKOkBLoEToablLhxPlZWrNak7Yu6Uw9jq?sEp[qcejTkPrZepguRnlCfqFbhBjh5e{EdMzUwMTe\iRo@H`betyNt^{_`XVoQe\>foGv}b|~anrl^sNqM^[Wy^MsLLh=O`@YsPsmPz@}\k}_nT[KipBm|;{NbcctKSj7QjUgrPlMnAiPcpePsy>Kq`{Y~Bd{ZvWxSfT]DVz;]Ws~ikQvLg9zPp@ZnKsxJtBw>UZfx6xTgHePhnNty@WDtuMP{HBoY[[x~2UI^u:Sv,MgGbna]E|rP~Yg7yMx~weȗ@ꈐݪ~qv]k^vY]\fx`jH|~CGbiv_zb|[U}lnHY~rhrAeisvtYk[TYr{=4ZSsnpO[cw`e[i|PmpwV`ڧf롁פLj~YPu__WMgPJ:\NwԋxoH_z}Cq[cOJxdCU]IIMIMu}pو|O`sZ|DmLnLwNujLr<FWarqgrVE_mEvWdw^H^V^Wd?spHqEpICq{ziqNCk{gC_7_XkMgEr[Tg8nYc~?vMVxao^mlq[r@qDavPvJxM^iEfLiCViKmDUoHtZmWTvO@kTe>du+UfKWsS|?jATsFg`iNrNuSffb6Vo4iHuT^FlCbDgOeViLvQb?rGmZk1Y2X5[FR{T~JlLa{EzaWjUgJu[tNeDjLmd`kEnU]F`DhL^LOvGl_vK:Z~ZjWmSUx`qrpdΆip`Uk]\u{g\SKp4[y@iKnFhROM|?yN]iwkL}eXcw;SrF[wLvbzZc|JaqRewJgxMwBApP``diTmxXNhRdYPiVCdR\aB`h5nn?^K_}UurQrLMVURi]t{\jxDkyG^tGxDfDhYiO~wD^]V|]tg?dBTi[f`Tj:exCxQYwdguLgsBxvJJferdiJRPYkVWvO\kFPh=xAPfUxK`FUIpCDhk}]aOC`|ϷԌUl\Rb{UVLƍ|vaJNb{ehqF>7xܦtWk\9rHSpiIiXeu~L\`oνbSXUbXTu껗wѺhu¦ۯSac۩yɭ3HOӲG~s\D^uudmmqDd}J}MuSoqFkXo~as\ozsK?pjswQ}\vhq`uRĒzkqMj1l=CGmY1dD^@}XiviYF^xUjZ@]uPPޔkap>]mj}YcbOgjpDcs3a},GtlzNmmLŠZS֨ziv]y{X9hhlSjds8b7W}<]Sxv^]atx;~Gpene]=SvT[i|TEPQXyXP{LWmBm_}~KqQ^q?F|hww~auehjUxz]_ku|hb|hd[ioA`>HzaQPqRm{lXqtDWySQnUuyyZsTuZ~Tсaq\rnykz_yIzLU]mueFnHj4rGpHxU{KRnQJfAhB׍ElKlH^HvEwGn]pZsNmMu=q[vZ}b_~UjTg}@k?eHfJk_EIXvA\xi=\{7[IkCeQoMbLaBlEmTmH`E^M`?\AeLcG}Q]?qZk[D`A[:iK|i]OZUwQo{WuPzR^Oe=YH^lKx?vCiLp@`4dD]bYb8KsMdIdIgRy]Y^nU}obbvluroYf\yxph\TyHcOd^pyvdQxTwf`lR{>sGhbvY^yNHaH=OIGXEN{<]fDdrIiXTlavbepBQ{RIeVRaFYl=VsAWoA`oD_xFiqWeOgCmZzP~\cqkrjzQOhP~^tChc\XksHYQ=vfWZ?Om6LkJaW=lo0V~qxLn~A{qANzX|TsOpCPObrLwzM~^bhOk`qad~{PoZ~zHGJgrcrzWb,n}?ElXlxt]maZnXSnkkaxEOcVa[r}J,SuZDP}bzn~@ZOuAU|ǐgWeRkMEm[UfbVh}J>tuxd|veWog~mLoTfxbxguL]|qszS\{U|cgsuHwlHrZCP[xnqCdd_³dlrmlmvLavV}K˔ꭦjʎhqp=zb`?:vilyVvUwD{A]gih}d|3.eòqhICk]Wcu@wTiNn]}}P7Xq\UrVPDEebFbks?pa^}ghv~LAfYG9oLTL|`ͫy}RlYUiwTG[aZ|BM˼aw[ul7A_PnXxE[JOhGGV|yӛ蠊uHQgArGsFKgHypwmUyTYz?kTd6qBaARTCn?pYpyShpVl_w^㚱\gEdWYBt4`f'yap}RbLhQpKtspj̆m[hdmSiXq^WqhjfNV}k~kt^t~=Rv4tRXhQqLdk\qidxՌVxSNYHfbVf[mNNmSo|goMKm@{L}HJw]gfbMfQhrvuSB`IrUoQj\cvXZ~Pi{VJNydVWs`Vċfr@XQhuDkDP^]dKz};mExDlX[~MstD|[kezOSbecyapf~[Td~kQ~dmL}G\vtcj|Ea~\Ld\ILOܫLe~`YMfdӖ򡣫fa~|pMwZս[QYhyIOjumI6x:ƅ^XɀbtDbP6`|H7cR}heuca{_wun|Ls9D?N{VN|?[zhdx~Rhagzh>ӞFib2^V=\\[oCzIu|zo1|px_كkTYwyOQ{e`LZjwZYoDmLYoItEBip80nE|=On{pTHNzLx\zoʓ[L|DrQduT^FY}pj|L7D]#En3{?v-Bh`tKsgՔeS;·RS[Fm(DayItnzSnKs4CnBpf~~qIpɂg~cSyCSzsTE_sAR@rXUngVedxUh@`h`BqNaa]nuLQPyGyS|coO{UrCs[fP{Yxf~TMlFNTv@`_XiRuahraHoLqzFSwV†zrdyzoi`^zSvQToPUnKmFk\UYbKpXgM`Jh]dN`HlbQiIbBbJdyCh\qAiWji}__g_V{XesckfXhOeSfEaMV~fw\eGrB\5Z{HzPg;^|CcTE{_vYh|x9pDfMiu@O}CQnOJlh`>}(mG]QSyROpSJh9v\FmSx~KnIYwvxqhniikzMj:rXxBciZx_VhWdEkT~JvGoOpsCIxdqLpI~OCgYn\Z^kypԫun_jOw䋎ppqdJmr\VsoƏ^iӪYkdOW:i2eW-[nGoxXfvfgVMVuN\fEq%mhG{A\OlkCkswoEtIH~hc_UXMLLzWrYN:pUWeF:[PEmZGڌ~G}ŸvxZrADh]uDl-\j,[{Vx@ҘM{6Rwi5_AZ{ixBq`xJTz/zEsD8`]SHui=DGo]XoQnZsHhnS_`W|ityW^hRbQybL}ðhsGqfQuKiI\~CX}QdEnLrGdzDe;Xs:[Hng|`cDdLWIKs5[IMiYs]bfdEojg\iWc}gnIpPd=]SfX{cjvUdXgxLjZxL|MQz^zZoNh8`~K}j~Volqfn`{>efvAlxDqLQJfYXe`d@[xAWpOLi9OmGliHjO}~JwBiTnwXcNge}QiKRSedGux?cE_Vin=yGr=z|\_Y\~fue4z5iZk_zzAU?iiLnp2h;~R[EK|e[f@~yAVh^O`O{zIjBxbsL~JpZjMouB3>Yf̀tˈn܀܌sxps\E{VcrHyVRIW]hsQw-fU`BzzSC}Wz^T~eH]IRClXZh|XkiNHuM~INO\bxgi~k~UXGuxfxo:}smb^^dzhUjeЅZ]dکLv⌞{rm^V[lo7tY?pLMJpImchu4@YeW}AX^tr%Cx}RiQ|8j{CQy0n8ǯSuq|knڇdv~{q5tIMohC~Uuhd3rercxBDYYT¤@Z`~Q?[sUIk:UwCS_b;GsuGժűEg}}bjlCCuJpL^MoQsGmxY\YhXrv8`YpQp[}y\>CD|>}Vbͻʖotru?LUlIz}hfqJPhcbBlPhxEm)gU~__˄}\Hi8xOV|TaUEa짐ٍcwavhjGovgayttQ{@oM|KvNkFrAy`iFwOjZhQzU{f\j^vaxflG{m[Ly`|U}YbNsjs[i~Fe^iW^rs_`Q}jlWwjwp=q1OEfykQFZTmnWompaT}\|riVS~]}ormkiOqLbFg~lU;rVoJrKR~2Mu?V=pJiMzDY=d=TwH\]mFrG{Lc;_=aLnK[JmIZ|Gy@o[`~?gS`daUoQ[@lUtH{m{zUp]vgbbrluvh[]QufWjH|Nf`Yz\|UfTsSzGyNu[thWeFcNiZ{feesUimڇȊoaMvNnfX?FlTwx=ag+LgGsMN`;BN0?D.eg>gz@a{ORrJdcKfhG[TOOJbHVdVY]K`i/Xt1hpBvx>XKXn[rqFPzev\iVEcBXO\F2Yf;o?y8k]b~uYrhbkZ[IhjAn{7qPkD~VxV{_ydmm]yaiBTycu_SE\M[lnBPrxOzbZj_ŨvTGʤ[Wqˈwc˅xUŠ_lq[ZtRc<|x6sxcM^`{IY|^m@ň?xƙPvRj9Dlgp}cYuyϟl|RYta꫘szUyt|R|NnFSK}vNBjRvcjÏLׄySbNykxQ[ɁkԬόZisGVM{aBgYChRoP}WeMJ^pGo~K`DhIi~Jefo|J_}TvCx\q\XyMUnl;vxRkPtbiNLWF]zC`Ofrntz]xËvKYjҏ蟤ƏjlvQRfaoTvNWaW}`xXsbkvyRmKbEXxS\ՌvHpco?VOs3`CmOjKsI^8Xr@{>^BYx>QoE^AkSdDiSzUi;\Gh@_CjKfIe?kKucmWX}EmWgTRxVr}{xo{WdAfTzU]SxmyYqZx]qMjYzH_zRdWkZzXvSpWvb}TwG`{XaE\~FvLf\gXhCdht}|͘ꂨuwpj`mO\tYB~LoI^z1Ul9You{E|CpI\|JupgSLP]iRlPfc̆gS}fc}cp[pKvFlP^o5pooaoe\n{MlpwpzPc>lNUwxzơcTqXfQQFxdikYtNjXvm}]j}cm~jTcpIgQ|^uRrE_yUubzYn?_z`lR~8aWwIUzN_Oqhnk~J_yttKǂ]lIXuOctZU}NvZ;SnM}`\}lsf|`qyrvi[vywn}H_adqo\HseanXbz|meJgdrGgIn;\s5ZlGj[`M_5`x<}EoJ`EpLfN{>hTq=]w>b8Ns3Ln8eImQbHVJmOuAcJoFuSmBsXwQg]nqz^x@gVgQjDnZs_g\pFlXyI~Xowwwa]qW]TcLk\zSN|StNfVqJ_}PrTwKsC[v>Tw<[Lka|u}LfJoOzWXeQs{ͅm~|Ć~v{wykfbm^DwFgO^p9^r:nrLcqFy=GwYrpmbm^QJQWjd[e9Yi>bfBeBlDjUgG_NsV}PUMzQToDi_asQnkEsBXRmn\qG\S-oMA;NJTOd=s`Gi=\DoKT|B_LcDNv?]]}J{>Z=zHr=iExK]DkIhdfDlRh_hIyZ{WxSmPsc|LmKg]wTpRobijepLmXYsKbXekgx]JbL_~ItUxYyj}UhGl\TdgWzE`t8e}?X~JpXv]j[lScVc^{Ld}Kg[w{qshgwlxezkxoip\xtRjG[{\>WfiqPvH}ODtV`dLSVbAcX6cn>osZo\MWqa|OPnvzyReacKNUJo\Og7Dh1b[Apy8\Pw;`y‘ɪthkYZ_yXSQrP^lcvfqU[iq__e\inv\eXndbGdzD^xGNmSm`36Wmhu[Uf]Z|Mb~;~wBnl~]~^VS[~|fgxLnVg#^P`regpkpFyܕr|xtGL{whqIc{UYMtbj~Wvh\j2u1}T\~`t)x<_p[iAXgY>j0VZvknmYyLZ{TZenGo[aMV{uo5bTmpKzJ?jV\I5cMn>yIGpHu4@XT[)ѬjTvA|FpjFctv]vQoGmvPSsi|Yxt[\Yz{B>lTU|=cOx5Z^?vEYZQLUtR[t{SMx?UaiMPz@ily:[ĉTatwFN˘[ΌvFCÎߙal^vXTVeMnG{ivPUOuGo?JZ:wOj˥mknIJmG~J9V^F^x{}Tw|kpWfWSYNTd`eyb]s}zHCHz@|C`YŊx~MaBjnwaيS}YuvzR|@b[WzI|TpmiX^srzbЃrMxBqbbcEv\}K~L{elb{esh`RbkAyPqbfnw^qW}h]Un|UtOQlull^}pylq^t[SV|/\u@i|BfP}nfgZH>QpJwIE_8]]$rd$m8\Kgx]yC~Kw]mu_aU|L=lq|gc9N{N_\Fyn.cG`yIt~[Ws^]YkeDkb+Zo>Qi?Oc=w^?Prz[WpkcdRvRgOOp;wg;d7_>w=4nbGR}[lқv^czJZS[rItjY0|5[_mPax֝^qficzDgVmdH{NnWfJx)<~mruiHǧl\;dOoC[Ng}S|{Bc~U|ehiy[e[_Fx0Py޹p~ōkmoovZp~EƋe~Xdvk<^Nkg`SdypoEuK[mwxRxt^7t{F`|W9ɀeͥg@f{JRsmE]\HYaCXmIbN>T]x[ndUepPbfRnz~u=XXWitfe݃c߷Xq;=HrRG{TWwtloNxiLYvoB|Jv|`rYd>d8:Lv״O…gw>eb^Aer29Ӆge]rsR`}eTyDukn:rm5|nEzP^{O]dvtYkVh[ytDp?VSej>_0jvA[LLPUfIhe9tAucucfgNmlyPh^^Cnp;\G[rHvTLZvekdrDM[OauPpr8Xp\kugUuh}WRokZh|Oj:hWxFxTfP\jcWN\^lVyDbř|zwzko`TRpotabt#x=msxXUg~vy=_8pQB`udlgwCp=RgwqdAf|mQNnXnbyRYEhQDgY|P_tB{eY|]s`s8IGM[Ðvj{nEdwO|ZRz>ҪuD䎟yxwXiK{?qhn~|J7rϔ[rZy\OQfnIwWowvl~XO͜d;FCuWatPg}8AqPUrBigyPTLuy\P\@zA鎉JfRrFogwo\?\W~CzydpfydRY~}gRxmNt|K_[fshZuX|DOnugTjB^E4OǾόJiu^}7v{@}Ѷ{zhp~T}L8?TB>qU䐨YLZzKoicSq7d~[i}ɗer\MfOj?m?DO~T۔zUXRuO4KzҘ^]WT`yHdBӋVZ5RZw`vr`{[Sijl_MJsOh^dOu6eMXa_8}`quZ@xiز|Ԟ`yrdfktelk}lrsh}kqpu]^U]nSXcoGhat`piGfts@zK}Tzrh@gUbkgPy|gt_nmSueBO~E~LlcXMtCa@jTtNKvrrgeWpWxTx_xX\}MkEiGj>oBiZnQWy4jMpUu]YpT{SqPw_\{5kJ\GpIu[йeTjTrLbGeiu]pa}dhC\YRnY}Pg@eXx]bbp<^zCgG^VqLiTlMgNjReNchdZhKWUjmbso\qJbNwopjait\~Zd[JbTl]}{}{jUt_tauXop?||CdkWhorxwm]RUrsfcukF̰iN_|nic8rpdcd}{VlD}T_\aAGgWpQtk|ln[b[kXOOi}dh@PhEtVA^dmrwڈҎx_iq|sq|C^odM[oti]k^tg?TmozeI{t]fXux[wP{gPey?^|=TyE5pZVutTebƏjzj̘ť~hdueiU~wfGf7csGVzcqNunle~rcTRWb^~>mTnaX6bKbInm]BGqARtIiIkRc{dkuqX[kJmfê鑐k|=KT\c\M}Idl\gHY:cAZIsYqdt[\wMWv=^y\oFwLZk~G|L`Sf^mOdLaDv:]w6gRjU}idt]guwO^zGbMmxuwNjNsPvUx_nbw\|XOeM]|Q}pkI`~JmCsPpLgCXx@iU}UvNho~WpJeMzf]j_oYgS~Rnx_w\qkpkjw{UZkhmon\WyaSCmDI[ESX1GrAGfMC[?Md~LGtΡgڢMZhäLTPBյ]xbAUItfQNb:N[i^Msp`ͅRvLJzsOoyqupvNUz`qXmf`qo/=zÁuENgӉcWZj3rBڡc|tq[zF].]t8Rx&VrIf^?wRpdhuprU~p[}=pe:`rIxJ_`^kLzHtnz:d?fFPd[mz?~NALsuyoLQhXY=Yp\g9r@esğ`X`blt{VgYfBtEzAjj2ih/QKe?uat^Eh}yEg]|GNYz؊mِIondAunrsfZ]@qelJf\ethU}l_^5x3DRl7[dr_uuitDy@|P\ـdT|tixʄtc[j{arqvVn\TcUwLj=y[Omqj;veSdyz~]`ep_PQUFX[t;dFe7[hLrDXajykr~~mar[xrV~Z`PpZtzo~n{rYvgFgBXJ~HNhPlR`WYXn[f}arctr=qOc}>[p|GxZxEyneOtLcTxFvlpdgexc\fh}In_giXpMiRfC\:WuiQzz]go`Fk8Oz?eToVfrIeI{GS|HgGhdh|gle|B\}KqXTpZjdga]B]L|nb`IaTYtKfVbHfBwae{dnyXcWhSfOhV[OZxPsRa\}]vva{fyvzujjXi[t_pdb\ryxcUdjWbQٕX}؍rsGsOmf1x>PTtn?\fxzYndYKo`{hxTu@|<~8~D҇EuzQ]qOףY|n|SQPcYl|YaژXOoQqTF\:tyPescMSna53wk`ui|WPfQ{p^?|}IJѳ׌Hzpc3Zx4j}FHYXA[cPvE}u<ʕGFyozH}P™|eis5Bw{Nb[gtGIpXM4seHwLZ4UxKqj/;ruuekXڭk]l6m~ATv\p__~9ju5bi[GTpSKxxRf]OA}_kًaؼ}θeq\xh{[aŽP]U_K`o?hGbFctqmtX~GoFJ\IY{F}YSkG{czmgclR[ZeJlYoR|VpQdy\jE^{YqLVwJlWmA^4mYsida:`wSTYUattY||wt[WnBjNT}JYrK_NbBaYb~ILnhtVfWvLYEykVlMjd}t}LaMscRiJydeTsI\Pi[hUmUq\mP~HYE]TnUhFWwGjFlGnX{djfu_|_Poahri~XrKi{NkXyff~fotzc}cd{f}j\^2Tf;WhI`jEhvDXzD[zGhp@pBnJi`Xu^XoIaoHg:kLRGKkNRhKLa:Qb;bg8jt@{HPdsqZYggq{X_q@ph?kA~hL]c{]x4ZzQk]bXn`mXmgOXRziG=P^~zh^wgx^K`Uhb\welzbkWxMwvJd]rpwx?}AjCzK{L\cfiV~L]Tw[^yStP7zzyq~`XqVn\nj*x<—bizճQmlôQWnE~lyokTsDQ>B|6hl&|(׺\ʒkW}@\TmQHUrzGTw}=YnmQltmxTIO|buC=a`TXr5ig8zyNmvkcsp{J>TOzp6oKjbwirj|z_bwNpnMb逈QgSd}J{eR3Y*|)TZYW9ttL6]VYw{wpRp>}]sNbqydrwP~țKUޓJz^v^‹{{pp]CµK}midXJKpEwEjFvGfTePrLmڅlSrYXH2}Emap^xoVgP{PxfbyMֻvHx~dVUP^vRv]HazVx:nSTa|]_[fQl\se]MnYv^qVvwyUqVpNj^ptxpNdOncO\Rn`o_lV~QPrYNlKlQ}GUPmK]yF\@]{OlElIh|OjVmSrTsTv[vQm]|^m^Jh{PQp`~^ZibpUcUa[}Pl[y]M]1UR0OcqyO]xPboCtk=n